Setup and some initial exploration

In this notebook we are planning to start with an object that has already been integrated. This integrated object has a column that indicates the original batch (currently labeled batch but will be renamed to “sample”) and a column that indicates cell types (celltype) in the colData.

We can also grab any other additional sample-level metadata from the metadata file for this dataset. We can use that to help identify groups of interest for differential expression.

In this first section, we explore the integrated object and identify libraries that would be good to use in setting up differential expression.

set.seed(2022)

library(magrittr)
library(SingleCellExperiment)
library(ggplot2)

The merged and integrated datasets are available on S3 right now (s3://sc-data-integration/scpca), but after deciding what exactly we will use as input here, we will create the proper setup scripts and move the data to the server starting with the individual SCE objects used to create the integrated dataset.

# set up file paths and read in combined SCE and integrated SCE 
data_dir <- file.path("..", "setup", "rms")

combined_sce_file <- file.path(data_dir, "dge", "SCPCP000005_merged_sce.rds")
combined_sce <- readr::read_rds(combined_sce_file)


integrated_sce_file <- file.path(data_dir, "dge", "SCPCP000005_integrated_fastmnn_sce.rds")
integrated_sce <- readr::read_rds(integrated_sce_file)
# put the integrated PCA and UMAP into the combined object so all the data is together 
reducedDim(combined_sce, "fastMNN_PCA") <- reducedDim(integrated_sce, "fastmnn_PCA")
reducedDim(combined_sce, "fastMNN_UMAP") <- reducedDim(integrated_sce, "fastmnn_UMAP")
reducedDimNames(combined_sce)
[1] "PCA"          "UMAP"         "fastMNN_PCA"  "fastMNN_UMAP"
# file path to sample level metadata 
sample_metadata_file <- file.path(data_dir, "rms_sample_metadata.tsv")

# read in sample level metadata to use for DE 
sample_metadata <- readr::read_tsv(sample_metadata_file)
Rows: 10 Columns: 10
── Column specification ───────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (10): project_id, submitter, library_id, sample_id, diagnosis, technology, seq_unit...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Let’s take the sample metadata file and add in the metadata to the colData of the combined SCE object. While doing this we can also do some modifying of the columns in the colData, such as creating a celltype_broad and celltype_fine column to use for plotting.

# collapse cell types 
coldata_df <- colData(combined_sce) %>%
  as.data.frame() %>%
  tibble::rownames_to_column("cell_id") %>%
  # remove any of the subtypes to create new cell type column 
  dplyr::mutate(celltype_broad = stringr::str_remove(celltype, "-[ABCD]$"),
                celltype_fine = celltype) %>%
  # merge with sample metadata 
  dplyr::left_join(sample_metadata, by = c("batch" = "library_id")) %>%
    # simplify subdiagnosis for later
  dplyr::mutate(diagnosis_groups = factor(
    dplyr::case_when(
      subdiagnosis == "Alveolar rhabdomyosarcoma" ~ "ARMS",
      subdiagnosis == "Embryonal rhabdomyosarcoma" ~ "ERMS"
    )))
Error: Column name `cell_id` must not be duplicated.
Run `rlang::last_error()` to see where the error occurred.

Let’s just do some initial exploration of the integrated dataset that we are working with.

# UMAP to show cell type and integration  
scater::plotReducedDim(combined_sce,
                       dimred = "fastMNN_UMAP",
                       colour_by = "batch",
                       point_size= 0.5,
                       point_alpha = 0.2)

# show cell types in the integrated dataset 
scater::plotReducedDim(combined_sce,
                       dimred = "fastMNN_UMAP",
                       colour_by = "celltype_broad",
                       point_size= 0.5, 
                       point_alpha = 0.4)


# facet the UMAP by sample to show the distribution of cell types across each sample
scater::plotReducedDim(combined_sce,
                       dimred = "fastMNN_UMAP",
                       colour_by = "celltype_broad",
                       point_size= 0.5, 
                       point_alpha = 0.4,
                       other_fields = "batch") +
  facet_wrap(~ batch, nrow = 3)

Looking at this plot lets us know which cell types belong to which sample and can help us pick which libraries we might want to consider for differential expression. From this we see that 6 of the libraries show a split between Tumor_Mesoderm, Tumor_Myoblast and Tumor_Myocyte. 3 libraries are mostly Tumor and they probably weren’t able to be classified into sub types. Then 1 library has what appears to be all NA. Let’s just look at the libraries that have myoblast, mesoderm, and myocytes and see if we can do some differential expression between those libraries.

Set up of dataset to use for integration

# define libraries to keep
scpca_library_ids <- c(
  "SCPCL000479",
  "SCPCL000480",
  "SCPCL000481",
  "SCPCL000484",
  "SCPCL000488",
  "SCPCL000491"
)

# subset sce to only contain batches with library IDs of interest 
batches_to_keep <- combined_sce$batch %in% scpca_library_ids
rms_sce <- combined_sce[,batches_to_keep]

Let’s make some of the same plots above and show the distribution of cell types.

# UMAP to show cell type and integration  
scater::plotReducedDim(rms_sce,
                       dimred = "fastMNN_UMAP",
                       colour_by = "batch",
                       point_size= 0.5,
                       point_alpha = 0.2)

scater::plotReducedDim(rms_sce,
                       dimred = "fastMNN_UMAP",
                       colour_by = "celltype_broad",
                       point_size= 0.5,  
                       point_alpha = 0.4,
                       other_fields = "batch") +
  facet_wrap(~ batch)

It looks like the pink cells, or the Tumor_Myoblast are the most dominant cell type across the libraries. This might be an interesting group to start with for differential expression. We can confirm this by looking at the total numbers of cells in each cell type.

as.data.frame(colData(rms_sce)) %>%
  dplyr::count(celltype_broad)

# can also print out a summary of the cell types across each library 
colData(rms_sce) %>%
  as.data.frame() %>%
  dplyr::count(batch, celltype_broad) %>%
  dplyr::group_split(batch)
<list_of<
  tbl_df<
    batch         : character
    celltype_broad: character
    n             : integer
  >
>[6]>
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]
NA

Tumor_Myoblast is definitely the top represented cell type across the libraries. So we want to look at myoblasts across our libraries and see if there are any changes in the myoblast population between samples of different types. To do this, we should probably look at our sample metadata to see what types of samples we are looking at.

# Let's look at some of the clinical sample metadata that we have 
table(rms_sce$diagnosis) # everything is RMS 

Rhabdomyosarcoma 
           26544 
table(rms_sce$subdiagnosis) # sample is split between ERMS and ARMS 

 Alveolar rhabdomyosarcoma Embryonal rhabdomyosarcoma 
                     14611                      11933 
# how many of each, 3 of each subdiagnosis, that's enough replicates so let's compare! 
colData(rms_sce) %>%
  as.data.frame() %>%
  dplyr::count(batch, subdiagnosis)

It looks like we have three samples that are from ARMS patients and three samples that are from ERMS patients.

A good question we could ask is if there are differences in gene expression patterns in the Myoblast population between ARMS and ERMS patients. If we want to ask that question, then we need to have replicates, preferably at least 3 samples for each type of patient, which we do!

Let’s go ahead and try it!

Differential Expression Analysis

Pseudo-bulking

Before we can do DE analysis to compare the gene expression profiles of myoblasts in ARMS vs. ERMS patients, we will need to “pseudo-bulk” the gene counts. Pseudo-bulking creates a new counts matrix that contains the total counts across all cells of a given label (e.g. cell type) for each sample. This allows us to use single-cell resolution to define the labels and sum gene counts across groups of cells containing the same label, and avoids counting each cell as its own replicate.

Below are a few reasons why pseudo-bulking is helpful:

  • Produces larger counts which are more amenable to standard normalization and differential expression methods used by bulk RNA-sequencing.
  • Collapses gene expression counts by sample, so that samples, rather than cells, represent replicates.
  • Masks variance within a sample to highlight variance across samples.

Let’s start with the simple comparison of looking at one cell type and comparing across ERMS and ARMS subtypes. When we do this, we will first want to pseudo-bulk our dataset to group our cells by cell type and by sample. Then we can subset the pseudo-bulked dataset to just contain our cell type of interest before proceeding.

# first let's subset our coldata to only have the columns we care about in pseudobulking 
pseudobulked_coldata <- colData(rms_sce)[, c("celltype_broad", "batch")]

# this creates a new SCE object that contains the pseudobulked counts across the provided groups 
pseudobulked_sce <- scater::aggregateAcrossCells(rms_sce, 
                                                  id = pseudobulked_coldata)

# column names aren't automatically added, so let's add them in 
colnames(pseudobulked_sce) <- paste(pseudobulked_sce$celltype_broad, 
                                    pseudobulked_sce$batch, sep = "-")

pseudobulked_sce
class: SingleCellExperiment 
dim: 573 37 
metadata(202): salmon_version-SCPCL000478 reference_index-SCPCL000478 ...
  variable_genes subset_hvg
assays(1): counts
rownames(573): ENSG00000278996 ENSG00000150672 ... ENSG00000135932
  ENSG00000079805
rowData names(30): gene_symbol-SCPCL000478 mean-SCPCL000478 ... mean-SCPCL000498
  detected-SCPCL000498
colnames(37): Fibroblast-SCPCL000488 Lymphocyte-SCPCL000484 ... Vascular
  Endothelium-SCPCL000488 Vascular Endothelium-SCPCL000491
colData names(23): cell_id sum ... batch ncells
reducedDimNames(4): PCA UMAP fastMNN_PCA fastMNN_UMAP
mainExpName: NULL
altExpNames(0):
# only 37 columns in the counts assay 
counts(pseudobulked_sce)[1:10, 1:10]
                Fibroblast-SCPCL000488 Lymphocyte-SCPCL000484 Lymphocyte-SCPCL000488
ENSG00000278996                     79                      0                    271
ENSG00000150672                    121                      9                     50
ENSG00000157168                      9                      4                     33
ENSG00000161671                     56                     38                    194
ENSG00000258399                     96                     28                    232
ENSG00000185532                   1428                     33                     76
ENSG00000157445                     52                     28                     93
ENSG00000198947                    415                      1                    170
ENSG00000245532                    631                      4                    458
ENSG00000155974                      8                     26                     51
                Monocyte-SCPCL000479 Monocyte-SCPCL000480 Monocyte-SCPCL000481
ENSG00000278996                   51                  224                   76
ENSG00000150672                    6                   23                  430
ENSG00000157168                    0                   14                  342
ENSG00000161671                    3                   14                   11
ENSG00000258399                    4                    6                   34
ENSG00000185532                    6                   72                  140
ENSG00000157445                   22                    2                    0
ENSG00000198947                    4                    4                   38
ENSG00000245532                   67                  358                  471
ENSG00000155974                    2                   15                   22
                Monocyte-SCPCL000484 Monocyte-SCPCL000488 Monocyte-SCPCL000491
ENSG00000278996                   43                  457                  262
ENSG00000150672                   34                  235                  455
ENSG00000157168                    2                  112                    1
ENSG00000161671                  226                  696                   85
ENSG00000258399                   33                  813                    0
ENSG00000185532                   13                  287                   11
ENSG00000157445                   16                  265                   41
ENSG00000198947                   18                  469                   25
ENSG00000245532                  265                 2513                  699
ENSG00000155974                    9                   96                   32
                Muscle-SCPCL000480
ENSG00000278996                209
ENSG00000150672                 54
ENSG00000157168                 33
ENSG00000161671                 53
ENSG00000258399                 55
ENSG00000185532               1208
ENSG00000157445                  2
ENSG00000198947                 44
ENSG00000245532                184
ENSG00000155974                 23

Let’s take a look at what the colData now looks like in the pseudo-bulked SCE object.

# note the new addition of the column with number of cells per group 
colData(pseudobulked_sce)
DataFrame with 37 rows and 23 columns
                                     cell_id       sum  detected subsets_mito_sum
                                 <character> <numeric> <integer>        <numeric>
Fibroblast-SCPCL000488                    NA        NA        NA               NA
Lymphocyte-SCPCL000484                    NA        NA        NA               NA
Lymphocyte-SCPCL000488                    NA        NA        NA               NA
Monocyte-SCPCL000479                      NA        NA        NA               NA
Monocyte-SCPCL000480                      NA        NA        NA               NA
...                                      ...       ...       ...              ...
Vascular Endothelium-SCPCL000480          NA        NA        NA               NA
Vascular Endothelium-SCPCL000481          NA        NA        NA               NA
Vascular Endothelium-SCPCL000484          NA        NA        NA               NA
Vascular Endothelium-SCPCL000488          NA        NA        NA               NA
Vascular Endothelium-SCPCL000491          NA        NA        NA               NA
                                 subsets_mito_detected subsets_mito_percent
                                             <integer>            <numeric>
Fibroblast-SCPCL000488                              NA                   NA
Lymphocyte-SCPCL000484                              NA                   NA
Lymphocyte-SCPCL000488                              NA                   NA
Monocyte-SCPCL000479                                NA                   NA
Monocyte-SCPCL000480                                NA                   NA
...                                                ...                  ...
Vascular Endothelium-SCPCL000480                    NA                   NA
Vascular Endothelium-SCPCL000481                    NA                   NA
Vascular Endothelium-SCPCL000484                    NA                   NA
Vascular Endothelium-SCPCL000488                    NA                   NA
Vascular Endothelium-SCPCL000491                    NA                   NA
                                             celltype       batch       celltype_broad
                                             <factor> <character>          <character>
Fibroblast-SCPCL000488                     Fibroblast SCPCL000488           Fibroblast
Lymphocyte-SCPCL000484                     Lymphocyte SCPCL000484           Lymphocyte
Lymphocyte-SCPCL000488                     Lymphocyte SCPCL000488           Lymphocyte
Monocyte-SCPCL000479                       Monocyte   SCPCL000479             Monocyte
Monocyte-SCPCL000480                       Monocyte   SCPCL000480             Monocyte
...                                               ...         ...                  ...
Vascular Endothelium-SCPCL000480 Vascular Endothelium SCPCL000480 Vascular Endothelium
Vascular Endothelium-SCPCL000481 Vascular Endothelium SCPCL000481 Vascular Endothelium
Vascular Endothelium-SCPCL000484 Vascular Endothelium SCPCL000484 Vascular Endothelium
Vascular Endothelium-SCPCL000488 Vascular Endothelium SCPCL000488 Vascular Endothelium
Vascular Endothelium-SCPCL000491 Vascular Endothelium SCPCL000491 Vascular Endothelium
                                        celltype_fine  project_id   submitter   sample_id
                                             <factor> <character> <character> <character>
Fibroblast-SCPCL000488                     Fibroblast SCPCP000005   dyer_chen SCPCS000262
Lymphocyte-SCPCL000484                     Lymphocyte SCPCP000005   dyer_chen SCPCS000258
Lymphocyte-SCPCL000488                     Lymphocyte SCPCP000005   dyer_chen SCPCS000262
Monocyte-SCPCL000479                       Monocyte   SCPCP000005   dyer_chen SCPCS000253
Monocyte-SCPCL000480                       Monocyte   SCPCP000005   dyer_chen SCPCS000254
...                                               ...         ...         ...         ...
Vascular Endothelium-SCPCL000480 Vascular Endothelium SCPCP000005   dyer_chen SCPCS000254
Vascular Endothelium-SCPCL000481 Vascular Endothelium SCPCP000005   dyer_chen SCPCS000255
Vascular Endothelium-SCPCL000484 Vascular Endothelium SCPCP000005   dyer_chen SCPCS000258
Vascular Endothelium-SCPCL000488 Vascular Endothelium SCPCP000005   dyer_chen SCPCS000262
Vascular Endothelium-SCPCL000491 Vascular Endothelium SCPCP000005   dyer_chen SCPCS000265
                                        diagnosis  technology    seq_unit
                                      <character> <character> <character>
Fibroblast-SCPCL000488           Rhabdomyosarcoma       10Xv3     nucleus
Lymphocyte-SCPCL000484           Rhabdomyosarcoma       10Xv3     nucleus
Lymphocyte-SCPCL000488           Rhabdomyosarcoma       10Xv3     nucleus
Monocyte-SCPCL000479             Rhabdomyosarcoma       10Xv3     nucleus
Monocyte-SCPCL000480             Rhabdomyosarcoma       10Xv3     nucleus
...                                           ...         ...         ...
Vascular Endothelium-SCPCL000480 Rhabdomyosarcoma       10Xv3     nucleus
Vascular Endothelium-SCPCL000481 Rhabdomyosarcoma       10Xv3     nucleus
Vascular Endothelium-SCPCL000484 Rhabdomyosarcoma       10Xv3     nucleus
Vascular Endothelium-SCPCL000488 Rhabdomyosarcoma       10Xv3     nucleus
Vascular Endothelium-SCPCL000491 Rhabdomyosarcoma       10Xv3     nucleus
                                           subdiagnosis  tissue_location    disease_timing
                                            <character>      <character>       <character>
Fibroblast-SCPCL000488           Alveolar rhabdomyosa..             Calf Initial diagnosis
Lymphocyte-SCPCL000484           Alveolar rhabdomyosa..            Thigh Initial diagnosis
Lymphocyte-SCPCL000488           Alveolar rhabdomyosa..             Calf Initial diagnosis
Monocyte-SCPCL000479             Embryonal rhabdomyos..         Prostate        Recurrence
Monocyte-SCPCL000480             Embryonal rhabdomyos.. Prostate/bladder        Recurrence
...                                                 ...              ...               ...
Vascular Endothelium-SCPCL000480 Embryonal rhabdomyos.. Prostate/bladder        Recurrence
Vascular Endothelium-SCPCL000481 Embryonal rhabdomyos..           Pelvis        Recurrence
Vascular Endothelium-SCPCL000484 Alveolar rhabdomyosa..            Thigh Initial diagnosis
Vascular Endothelium-SCPCL000488 Alveolar rhabdomyosa..             Calf Initial diagnosis
Vascular Endothelium-SCPCL000491 Alveolar rhabdomyosa..            Chest        Recurrence
                                 diagnosis_groups       celltype_broad       batch
                                         <factor>          <character> <character>
Fibroblast-SCPCL000488                       ARMS           Fibroblast SCPCL000488
Lymphocyte-SCPCL000484                       ARMS           Lymphocyte SCPCL000484
Lymphocyte-SCPCL000488                       ARMS           Lymphocyte SCPCL000488
Monocyte-SCPCL000479                         ERMS             Monocyte SCPCL000479
Monocyte-SCPCL000480                         ERMS             Monocyte SCPCL000480
...                                           ...                  ...         ...
Vascular Endothelium-SCPCL000480             ERMS Vascular Endothelium SCPCL000480
Vascular Endothelium-SCPCL000481             ERMS Vascular Endothelium SCPCL000481
Vascular Endothelium-SCPCL000484             ARMS Vascular Endothelium SCPCL000484
Vascular Endothelium-SCPCL000488             ARMS Vascular Endothelium SCPCL000488
Vascular Endothelium-SCPCL000491             ARMS Vascular Endothelium SCPCL000491
                                    ncells
                                 <integer>
Fibroblast-SCPCL000488                  62
Lymphocyte-SCPCL000484                  11
Lymphocyte-SCPCL000488                 127
Monocyte-SCPCL000479                    21
Monocyte-SCPCL000480                    76
...                                    ...
Vascular Endothelium-SCPCL000480       119
Vascular Endothelium-SCPCL000481        59
Vascular Endothelium-SCPCL000484        13
Vascular Endothelium-SCPCL000488       269
Vascular Endothelium-SCPCL000491        67

Before we can perform DGE analysis, we want to do some quality control like filtering out any groups in the pseudo-bulked dataset that may have a low number of cells.

# filter by number of cells 
filtered_pseudobulked_sce <- pseudobulked_sce[, pseudobulked_sce$ncells >= 10]

We also want to filter out any genes that may be lowly expressed in our pseudo-bulked dataset.

# filter out genes that might be lowly expressed using filterbyExpr in `edgeR`
# provide sample groups from SCE to determine minimum sample size that genes should be expressed in 
# we are grouping samples as ERMS vs. ARMS so use subdiagnosis
genes_filter <- edgeR::filterByExpr(filtered_pseudobulked_sce,
                                     group = filtered_pseudobulked_sce$subdiagnosis)
filtered_pseudobulked_sce <- filtered_pseudobulked_sce[genes_filter,]

# looks like this doesn't actually filter anything though, but we probably want to keep this step in? it seems important 

Maybe we should explore changing defaults in instruction? Adding the defaults here for future reference.

  • min.count = 10: Minimum count required for at least some samples.
  • min.total.count = 15: Minimum total count required.
  • large.n = 10: Number of samples per group that is considered to be “large”.
  • min.prop = 0.7: Minimum proportion of samples in the smallest group that express the gene.

The last filtering will be to filter to just the sub type of cells that we are interested in.

tumor_myoblast_sce <- filtered_pseudobulked_sce[, filtered_pseudobulked_sce$celltype_broad == "Tumor_Myoblast"]
table(tumor_myoblast_sce$celltype_broad)

Tumor_Myoblast 
             6 

We should now see that there are only 6 samples left that all have the cell type Tumor_Myoblast.

Perform differential expression with edgeR

Note that at this point we can treat our data set like bulk RNA-seq samples where each library is a sample and use the same methods we would use for bulk, such as edgeR and DESeq2. First let’s try and do some DE analysis with edgeR.

dge_list <- edgeR::DGEList(counts(tumor_myoblast_sce),
                    samples = colData(tumor_myoblast_sce))

# make column names equal to batch ids, helpful for plotting later 
colnames(dge_list) <- dge_list$samples$batch
dge_list
An object of class "DGEList"
$counts
                SCPCL000479 SCPCL000480 SCPCL000481 SCPCL000484 SCPCL000488 SCPCL000491
ENSG00000278996        3328        3214        3469        5496        8004       13768
ENSG00000150672        2772        7808      139767        7272        7892       44982
ENSG00000157168        3619        5494       87515        2693        6692        2034
ENSG00000161671         536        4758        4590       40668       31449       21699
ENSG00000258399        5464        4875        8841       16016       49919         218
568 more rows ...

$samples
NA

Now we need to normalize these counts!

Use the trimmed mean of M-values method to compute normalization factors. Counts are now large enough to apply bulk normalization methods rather than the deconvolution method that was used on individual libraries.

# compute some normalization factors 
dge_list <- edgeR::calcNormFactors(dge_list)
# should now see a new column, `norm.factors`
dge_list$samples

Before we do any DGE, let’s do some QC checks to make sure our data looks okay. We make some Mean difference plots, but I’m not really convinced we get a whole lot from these?

# mean difference plots
purrr::walk(1:ncol(dge_list), 
            ~ limma::plotMD(dge_list, column = .x))

# MDS scaling plot 
# first compute counts per million 
counts_per_million <- edgeR::cpm(dge_list, log = TRUE)

limma::plotMDS(counts_per_million,
        # add some colors based on our two groups
        col = ifelse(dge_list$samples$diagnosis_groups == "ARMS", "black", "red"))

Let’s do some DE now!

# set up design matrix to compare between ARMS and ERMS groups
design_matrix <- model.matrix(~factor(subdiagnosis),
                              dge_list$samples)
design_matrix
            (Intercept) factor(subdiagnosis)Embryonal rhabdomyosarcoma
SCPCL000479           1                                              1
SCPCL000480           1                                              1
SCPCL000481           1                                              1
SCPCL000484           1                                              0
SCPCL000488           1                                              0
SCPCL000491           1                                              0
attr(,"assign")
[1] 0 1
attr(,"contrasts")
attr(,"contrasts")$`factor(subdiagnosis)`
[1] "contr.treatment"

Estimate dispersions using both the quasi-likelihood(QL) dispersion and the negative binomial(NB) dispersions.

# estimate NB dispersions 
dge_list <- edgeR::estimateDisp(dge_list, design = design_matrix)
summary(dge_list$trended.dispersion)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.2358  0.2627  0.2665  0.2881  0.3085  0.4846 
edgeR::plotBCV(dge_list)

# estimate the quasi-likelihood dispersions
glm_fit <- edgeR::glmQLFit(dge_list, design_matrix, robust = TRUE)
edgeR::plotQLDisp(glm_fit)

Test for differences using the glmQLFTest where differentially expressed gens are defined as those with non-zero LFC and a FDR of < 5%.

results <- edgeR::glmQLFTest(glm_fit, coef = ncol(design_matrix))
summary(limma::decideTests(results))
       factor(subdiagnosis)Embryonal rhabdomyosarcoma
Down                                               30
NotSig                                            524
Up                                                 19
edger_results <- edgeR::topTags(results, n="Inf")$table
edger_results_sig <- edger_results %>%
  dplyr::filter(FDR <= 0.05)

# print out results 
edger_results_sig

Perform differential expression with DESeq2

Let’s do the same thing, but try with DESeq2.

# deseq2 requires the coldata object as a separate input
coldata_df <- as.data.frame(colData(tumor_myoblast_sce))
  
# set up the deseq object 
deseq_object <- DESeq2::DESeqDataSetFromMatrix(counts(tumor_myoblast_sce),
                                               colData = coldata_df,
                                               design = ~ diagnosis_groups)
converting counts to integer mode

Use the median of ratios method for count normalization followed by regularized log transformation.

# we get a warning if we don't use local that it can't find a model
# this could likely be due to batch effects present in the data
normalized_object <- DESeq2::rlog(deseq_object, blind = TRUE, fit = 'local')

Evaluate QC here using PCA. Here we can label by the diagnosis groups to show that the highest amount of variance is due to different diagnosis types.

DESeq2::plotPCA(normalized_object, intgroup = "diagnosis_groups")

We can also label by other metadata to see if there are other factors that might be related to variation.

DESeq2::plotPCA(normalized_object, intgroup = "disease_timing")

# run DESeq
# use the same fit type as with rlog 
deseq_object <- DESeq2::DESeq(deseq_object, fitType = 'local')
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing

Next we take a look at the dispersions, we expect to see dispersions decrease as means are increasing and follow the line of best fit. Here dispersions are increasing as mean is increasing but are following the line of best fit, so not the most ideal… perhaps underlying batch effects are here and affecting this?

plotDispEsts(deseq_object)

deseq_results <- DESeq2::results(deseq_object, alpha = 0.05)
DESeq2::resultsNames(deseq_object)
[1] "Intercept"                     "diagnosis_groups_ERMS_vs_ARMS"
shrink_results <- DESeq2::lfcShrink(deseq_object, res = deseq_results, coef = 2, type = "apeglm")
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
    Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
    sequence count data: removing the noise and preserving large differences.
    Bioinformatics. https://doi.org/10.1093/bioinformatics/bty895
# look at what actually happened when we applyed shrinkage (adjustment of large fold changes that are overestimated)
comparison_df <- data.frame(
  lfc_original = deseq_results$log2FoldChange,
  lfc_shrunken = shrink_results$log2FoldChange,
  logmean = log10(deseq_results$baseMean)
  )

ggplot(comparison_df, 
       aes(x = lfc_original, 
           y = lfc_shrunken, 
           color = logmean)) +
  geom_point(alpha = 0.1) +
  theme_bw() +
  scale_color_viridis_c() + 
  coord_cartesian(xlim = c(-10,10), ylim = c(-10,10))

Let’s get a table of the genes that are significant.

deseq_results <- shrink_results %>%
  as.data.frame() %>%
  tibble::rownames_to_column("ensembl_id")

deseq_results_sig <- deseq_results %>%
  dplyr::filter(padj <= 0.05)

deseq_results_sig

Evaluating the DGE Results

Comparing methods

Before we move on, let’s compare the results between the two types of DEG analysis we did. Here we can look at the correlation between the logFC calculated with edgeR and the logFC calculated with DESeq2, which we expect to be similar. We can then see which genes are calculated as significant with each method and how they compare.

edger_results <- edger_results %>%
  tibble::rownames_to_column("ensembl_id") %>%
  dplyr::select(ensembl_id, logFC, FDR) %>%
  dplyr::rename("edgeR_LogFC" = "logFC",
                "edgeR_padj" = "FDR")

combined_results <- deseq_results %>%
  dplyr::select(ensembl_id, log2FoldChange, padj) %>%
  dplyr::rename("DESeq_LogFC" = "log2FoldChange",
                "DESeq_padj" = "padj") %>%
  dplyr::inner_join(edger_results, by = c("ensembl_id")) %>%
  dplyr::mutate(
    significance = dplyr::case_when(DESeq_padj <= 0.05 & edgeR_padj > 0.05 ~ "DESeq",
                                    DESeq_padj > 0.05 & edgeR_padj <= 0.05 ~ "edgeR",
                                    DESeq_padj <= 0.05 & edgeR_padj <= 0.05 ~ "both",
                                    DESeq_padj > 0.05 & edgeR_padj > 0.05 ~ "None")
  )
ggplot(combined_results, aes(x = DESeq_LogFC, y = edgeR_LogFC, color = significance)) + 
  geom_point(alpha = 0.5) +
  theme_bw()

Here we see that it looks like edgeR takes a more conservative approach and that any genes significant in edgeR are also significant in DESeq. We also see that there’s an extra subset of genes (with lower abs(log fold changes)) that are identified as differentially expressed in DESeq only.

Exploring the identified differentially expressed genes

Let’s also do some initial analysis to look at what types of genes are being identified and see if what is being identified as actually up/down regulated appear to correlate back to the single cell data.

The first plot we’ll make is a volcano plot and we can label the significant genes with their gene symbols. For now, let’s use the DESeq results.

deseq_df <- shrink_results %>%
  as.data.frame() %>%
  tibble::rownames_to_column("ensembl_id")

sce_rowdata_df <- rowData(tumor_myoblast_sce) %>%
  as.data.frame() %>%
  tibble::rownames_to_column("ensembl_id")

deseq_df <- deseq_df %>%
  dplyr::left_join(sce_rowdata_df, by = "ensembl_id")
EnhancedVolcano::EnhancedVolcano(deseq_df,
                x = 'log2FoldChange', # fold change statistic to plot
                y = 'pvalue', # significance values
                lab = deseq_df$gene_symbol.SCPCL000478, # labels for points
                pCutoff = 1e-05, # The p value cutoff we will use (default)
                FCcutoff = 1, # The fold change cutoff (default)
                title = NULL, # no title
                subtitle = NULL, # or subtitle
                caption = NULL, # or caption
                labSize = 3  # smaller labels
                ) +
  # change the overall theme
  theme_classic() +
  # move the legend to the bottom
  theme(legend.position = "bottom")

Let’s make some UMAPs where we plot a gene that’s identified to be differentially expressed in a given cell type and see what the expression of that gene is across samples.

# add column to colData with expression of gene of interest 
# here we will actually put all of the significant genes so then we can plot any of them 
sig_expressed_genes <- deseq_df %>%
  dplyr::filter(pvalue < 1e-5,
                abs(log2FoldChange) > 2) %>%
  dplyr::pull(ensembl_id)

sig_genes_counts <- logcounts(combined_sce[sig_expressed_genes,]) %>%
  as.matrix() %>%
  t() %>%
  as.data.frame() %>%
  dplyr::mutate(cell_id = colnames(combined_sce))
  
combined_coldata_df <- as.data.frame(colData(combined_sce)) %>%
  dplyr::left_join(sig_genes_counts, by = "cell_id")

# add back modified coldata containing gene expression for sig genes 
colData(combined_sce) <- DataFrame(combined_coldata_df, row.names = combined_coldata_df$cell_id)

The first plot we will make is with SOX5 which is found in both edgeR and DESeq

# filter to just myoblast cells and remove any NA's before plotting
myoblast_combined_sce <- combined_sce[, which(combined_sce$celltype_broad == "Tumor_Myoblast")]

# first look at just ARMS vs. ERMS 
scater::plotReducedDim(myoblast_combined_sce,
                       dimred = "fastMNN_UMAP",
                       colour_by = "ENSG00000134532", #SOX5
                       point_size= 0.5,
                       point_alpha = 0.4,
                       other_fields = "diagnosis_groups") +
  facet_wrap(~ diagnosis_groups, nrow = 3)

What about some genes that are differentially expressed in only DESeq but not edgeR?

# pick out the top genes that are identified as DE in only DESeq and plot those 
combined_results %>%
  dplyr::filter(significance == "DESeq") %>%
  dplyr::arrange(DESeq_LogFC) 
# top downregulated gene 
scater::plotReducedDim(myoblast_combined_sce,
                       dimred = "fastMNN_UMAP",
                       colour_by = "ENSG00000182197", #EXT1
                       point_size= 0.5,
                       point_alpha = 0.4,
                       other_fields = c("diagnosis_groups", "batch")) +
  facet_wrap(~ batch + diagnosis_groups, nrow = 3)

scater::plotReducedDim(myoblast_combined_sce,
                       dimred = "fastMNN_UMAP",
                       colour_by = "ENSG00000161671", #EXT1
                       point_size= 0.5,
                       point_alpha = 0.4,
                       other_fields = c("diagnosis_groups", "batch")) +
  facet_wrap(~ batch + diagnosis_groups, nrow = 3)

Differential expression of a secondary cell type

We can also look at other cell types in our dataset and set up the same comparison across disease states to identify differentially expressed genes. For simplicity we will just use DESeq2 here.

# pick out the next most prominent cell type to use 
colData(filtered_pseudobulked_sce) %>%
  as.data.frame() %>%
  dplyr::group_by(celltype_broad) %>%
  dplyr::summarise(total_cells = sum(ncells))

From this table, it looks like the tumor sub types are going to be the most promising.

We’re going to repeat the same analysis we did above just using the myocytes instead. I couldn’t copy and paste since we were doing it again, so here’s a function to repeat the analysis and we can run it across all cell types of interest in the analysis.

run_deseq <- function(sce, celltype){
  
  # subset to cell type of interest
  celltype_sce <- sce[, sce$celltype_broad == celltype]
  
  coldata_df <- as.data.frame(colData(celltype_sce))
  
  # create deseq object
  deseq_object <- DESeq2::DESeqDataSetFromMatrix(counts(celltype_sce),
                                               colData = coldata_df,
                                               design = ~ diagnosis_groups)
  # perform deseq
  deseq_object <- DESeq2::DESeq(deseq_object, fitType = 'local')
  
  # extract results
  deseq_results <- DESeq2::results(deseq_object, 
                                   alpha = 0.05)
  # shrink and set up data frame
  shrink_results <- DESeq2::lfcShrink(deseq_object, 
                                      res = deseq_results, 
                                      coef = 2, 
                                      type = "apeglm") %>%
    as.data.frame() %>%
    tibble::rownames_to_column("ensembl_id")
  
  # get rowdata from sce to get gene symbol 
  sce_rowdata_df <- rowData(celltype_sce) %>%
    as.data.frame() %>%
    tibble::rownames_to_column("ensembl_id") %>%
    # only select columns we want, the gene symbol column will be modified in the setup script 
    dplyr::select(ensembl_id, gene_symbol.SCPCL000478) %>%
    dplyr::rename(gene_symbol = gene_symbol.SCPCL000478)
  
  # join together by ensembl id, adding in rowdata includes gene symbol 
  deseq_results_df <- shrink_results %>%
    dplyr::left_join(sce_rowdata_df, by = "ensembl_id")
  
  return(deseq_results_df)
  
}

We already added the counts for some genes to the combined_sce object so we can use that to make some plots. Starting with FOXO3, in the same family as the fusion partner (PAX3/PAX7-FOXO1) found in ARMS.

# subset to just tumor celltypes that we are interested in
tumor_sce <- combined_sce[, which(combined_sce$celltype_broad %in% celltypes)]

# violin plot across cell types 
scater::plotExpression(tumor_sce,
                       features = "ENSG00000118689", #FOXO3
                       x = "diagnosis_groups", 
                       colour_by = "diagnosis_groups",
                       other_fields = "celltype_broad",
                       point_size = 0.1) +
  facet_wrap(~ celltype_broad)

As the differential expression results showed, FOXO1 is up regulated in ARMS in mesoderm cells and myoblasts, but appears to be almost equally highly expressed in myocytes in both disease sub types.

Differential abundance of cell types

In addition to looking at differential expression of genes across cell types, we can also compare the distribution of cell types in each sample and compare across conditions. In this analysis, we quantify the number of cells assigned to each cell type and measure if the abundance of cells assigned to a specific label changes in conditions (e.g. in ARMS vs. ERMS).

The example that we are going to use is through the edgeR package following the guidance in Orchestrating Single Cell Analysis.

Here we need to set up an object where the counts are not reads per gene, but cells per label.

# first remove any samples with NA
filtered_combined_sce <- combined_sce[, !is.na(combined_sce$celltype_broad)]
# create a table of cell abundances 
# treat each cell type as a gene 
cell_abundance <- table(filtered_combined_sce$celltype_broad, filtered_combined_sce$batch) %>%
  unclass()


# use original sample metadata to grab diagnosis for each library 
sample_metadata <- sample_metadata %>%
  dplyr::select(library_id, subdiagnosis) %>%
  dplyr::mutate(diagnosis_groups = factor(
    dplyr::case_when(
      subdiagnosis == "Alveolar rhabdomyosarcoma" ~ "ARMS",
      subdiagnosis == "Embryonal rhabdomyosarcoma" ~ "ERMS"
    ))) %>%
  # filter out any NA cell types
  dplyr::filter(library_id %in% colnames(cell_abundance)) %>%
  DataFrame()
rownames(sample_metadata) <- sample_metadata$library_id

# set up DGElist
abundance_dge_list <- edgeR::DGEList(cell_abundance, samples = sample_metadata)

Filter out any labels that are low abundance.

celltypes_keep <- edgeR::filterByExpr(abundance_dge_list, group = abundance_dge_list$samples$diagnosis_groups)
abundance_dge_list <- abundance_dge_list[celltypes_keep, ]

Set up the design matrix the same way you would with differential expression analysis and perform the analysis.

design_matrix <- model.matrix(~factor(diagnosis_groups), abundance_dge_list$samples)
abundance_dge_list <- edgeR::estimateDisp(abundance_dge_list, design_matrix, trend = "none")

abundance_fit <- edgeR::glmQLFit(abundance_dge_list, robust = TRUE, abundance.trend = FALSE)
abundance_results <- edgeR::glmQLFTest(abundance_fit, coef = 2)

# print out results and summary
abundance_results
An object of class "DGELRT"
$coefficients
                     (Intercept) factor(diagnosis_groups)ERMS
Lymphocyte             -4.777493                   -0.5206590
Monocyte               -2.937735                   -1.1421817
Tumor_Mesoderm         -4.103203                    2.0360792
Tumor_Myoblast         -1.140934                    0.5505157
Tumor_Myocyte          -2.100419                   -0.2827945
Vascular Endothelium   -3.614289                   -0.4755841

$fitted.values
                      
                       SCPCL000478 SCPCL000479 SCPCL000480 SCPCL000481 SCPCL000484
  Lymphocyte              16.25586    10.65656    22.21319    26.08197    24.30173
  Monocyte                55.18556    36.17701    75.40957    88.54336    82.49979
  Tumor_Mesoderm         413.62008   271.14953   565.20064   663.63943   618.34240
  Tumor_Myoblast        1811.34781  1187.43296  2475.15774  2906.24633  2707.87909
  Tumor_Myocyte          301.50170   197.65009   411.99391   483.74929   450.73075
  Vascular Endothelium    54.63790    35.81799    74.66121    87.66466    81.68107
                      
                       SCPCL000488 SCPCL000491 SCPCL000495 SCPCL000498
  Lymphocyte              49.24475    30.61228    40.90586    44.21122
  Monocyte               310.87677   193.25202   258.23427   279.10061
  Tumor_Mesoderm          96.80953    60.18023    80.41623    86.91418
  Tumor_Myoblast        1875.52465  1165.89258  1557.93155  1683.81855
  Tumor_Myocyte          718.39266   446.57833   596.74321   644.96241
  Vascular Endothelium   157.95726    98.19183   131.20947   141.81171

$deviance
          Lymphocyte             Monocyte       Tumor_Mesoderm       Tumor_Myoblast 
           10.674925             1.135274            11.560367            14.031233 
       Tumor_Myocyte Vascular Endothelium 
           13.871089             1.197436 

$method
[1] "oneway"

$unshrunk.coefficients
                     (Intercept) factor(diagnosis_groups)ERMS
Lymphocyte             -4.780807                   -0.5229791
Monocyte               -2.938213                   -1.1433249
Tumor_Mesoderm         -4.104864                    2.0375729
Tumor_Myoblast         -1.140966                    0.5505533
Tumor_Myocyte          -2.100594                   -0.2828701
Vascular Endothelium   -3.615285                   -0.4762262

$df.residual
[1] 7 7 7 7 7 7

$design
            (Intercept) factor(diagnosis_groups)ERMS
SCPCL000478           1                            1
SCPCL000479           1                            1
SCPCL000480           1                            1
SCPCL000481           1                            1
SCPCL000484           1                            1
SCPCL000488           1                            0
SCPCL000491           1                            0
SCPCL000495           1                            0
SCPCL000498           1                            0
attr(,"assign")
[1] 0 1
attr(,"contrasts")
attr(,"contrasts")$`factor(diagnosis_groups)`
[1] "contr.treatment"


$offset
         [,1]     [,2]     [,3]     [,4]     [,5]    [,6]     [,7]    [,8]     [,9]
[1,] 8.092239 7.669962 8.404472 8.565031 8.494334 8.67761 8.202208 8.49208 8.569786
attr(,"class")
[1] "CompressedMatrix"
attr(,"Dims")
[1] 6 9
attr(,"repeat.row")
[1] TRUE
attr(,"repeat.col")
[1] FALSE

$dispersion
[1] 3.863155

$prior.count
[1] 0.125

$df.residual.zeros
[1] 7 7 7 7 7 7

$df.prior
[1] 2.532037 2.532037 2.532037 2.532037 2.532037 2.532037

$var.post
          Lymphocyte             Monocyte       Tumor_Mesoderm       Tumor_Myoblast 
           1.2796751            0.2788763            1.3725662            1.6317832 
       Tumor_Myocyte Vascular Endothelium 
           1.6149826            0.2853977 

$var.prior
[1] 0.6014862

$samples

$AveLogCPM
[1] 12.76116 15.02523 16.25104 18.77918 16.69355 14.40420

$table

$comparison
[1] "factor(diagnosis_groups)ERMS"

$df.test
[1] 1 1 1 1 1 1

$df.total
[1] 9.532037 9.532037 9.532037 9.532037 9.532037 9.532037
summary(decideTests(abundance_results))
       factor(diagnosis_groups)ERMS
Down                              0
NotSig                            6
Up                                0

It doesn’t look like any of the populations are differentially abundant between the two conditions. I did find this statement from the paper, “ARMS tumors contained significantly fewer cells with the mesoderm gene expression signature and were skewed towards the myocyte signature.” However, they do show quite a bit of variability in the supplemental figures across the samples and they only show them for the PDX samples while the ones we are analyzing are the patient samples. We do see a positive fold change trending towards an increase in the mesoderm population in ERMS and a decrease in myocytes in ERMS, aligning with the findings in the paper (although not significant with these samples).

Some conclusions and future thoughts

  • The RMS dataset seems like a good dataset we can use for differential expression analysis
  • We probably don’t want to blindly use all libraries that we have currently used for integration, but should use the 6 samples that have varying sub diagnosis and also share similar cell types
  • Here we did some preliminary comparisons using edgeR and DESeq, but we probably don’t need to actually do that in class, we should pick one and show how you would do the analysis first to look at one cell type between experimental conditions and then across multiple cell types/ labels
  • edgeR is a bit more conservative in its approach to identify differentially expressed genes
  • Looking across tumor subtypes identifies different groups of differentially expressed genes
  • A preliminary look at cell type composition changes reveals some changes consistent with publication, but none that are statistically significant in this specific cohort

Session Info

sessionInfo()
LS0tCnRpdGxlOiAnU2V0dXA6IERpZmZlcmVudGlhbCBFeHByZXNzaW9uIE1vZHVsZScKYXV0aG9yOiAiQWxseSBIYXdraW5zIGZvciB0aGUgRGF0YSBMYWIiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiBubwotLS0KCgojIyBTZXR1cCBhbmQgc29tZSBpbml0aWFsIGV4cGxvcmF0aW9uCgpJbiB0aGlzIG5vdGVib29rIHdlIGFyZSBwbGFubmluZyB0byBzdGFydCB3aXRoIGFuIG9iamVjdCB0aGF0IGhhcyBhbHJlYWR5IGJlZW4gaW50ZWdyYXRlZC4gClRoaXMgaW50ZWdyYXRlZCBvYmplY3QgaGFzIGEgY29sdW1uIHRoYXQgaW5kaWNhdGVzIHRoZSBvcmlnaW5hbCBiYXRjaCAoY3VycmVudGx5IGxhYmVsZWQgYGJhdGNoYCBidXQgd2lsbCBiZSByZW5hbWVkIHRvICJzYW1wbGUiKSBhbmQgYSBjb2x1bW4gdGhhdCBpbmRpY2F0ZXMgY2VsbCB0eXBlcyAoYGNlbGx0eXBlYCkgaW4gdGhlIGBjb2xEYXRhYC4KCldlIGNhbiBhbHNvIGdyYWIgYW55IG90aGVyIGFkZGl0aW9uYWwgc2FtcGxlLWxldmVsIG1ldGFkYXRhIGZyb20gdGhlIG1ldGFkYXRhIGZpbGUgZm9yIHRoaXMgZGF0YXNldC4gCldlIGNhbiB1c2UgdGhhdCB0byBoZWxwIGlkZW50aWZ5IGdyb3VwcyBvZiBpbnRlcmVzdCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uIAoKSW4gdGhpcyBmaXJzdCBzZWN0aW9uLCB3ZSBleHBsb3JlIHRoZSBpbnRlZ3JhdGVkIG9iamVjdCBhbmQgaWRlbnRpZnkgbGlicmFyaWVzIHRoYXQgd291bGQgYmUgZ29vZCB0byB1c2UgaW4gc2V0dGluZyB1cCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4gCgoKYGBge3J9CnNldC5zZWVkKDIwMjIpCgpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKVGhlIG1lcmdlZCBhbmQgaW50ZWdyYXRlZCBkYXRhc2V0cyBhcmUgYXZhaWxhYmxlIG9uIFMzIHJpZ2h0IG5vdyAoYHMzOi8vc2MtZGF0YS1pbnRlZ3JhdGlvbi9zY3BjYWApLCBidXQgYWZ0ZXIgZGVjaWRpbmcgd2hhdCBleGFjdGx5IHdlIHdpbGwgdXNlIGFzIGlucHV0IGhlcmUsIHdlIHdpbGwgY3JlYXRlIHRoZSBwcm9wZXIgc2V0dXAgc2NyaXB0cyBhbmQgbW92ZSB0aGUgZGF0YSB0byB0aGUgc2VydmVyIHN0YXJ0aW5nIHdpdGggdGhlIGluZGl2aWR1YWwgU0NFIG9iamVjdHMgdXNlZCB0byBjcmVhdGUgdGhlIGludGVncmF0ZWQgZGF0YXNldC4gCgpgYGB7cn0KIyBzZXQgdXAgZmlsZSBwYXRocyBhbmQgcmVhZCBpbiBjb21iaW5lZCBTQ0UgYW5kIGludGVncmF0ZWQgU0NFIApkYXRhX2RpciA8LSBmaWxlLnBhdGgoIi4uIiwgInNldHVwIiwgInJtcyIpCgpjb21iaW5lZF9zY2VfZmlsZSA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJkZ2UiLCAiU0NQQ1AwMDAwMDVfbWVyZ2VkX3NjZS5yZHMiKQpjb21iaW5lZF9zY2UgPC0gcmVhZHI6OnJlYWRfcmRzKGNvbWJpbmVkX3NjZV9maWxlKQoKCmludGVncmF0ZWRfc2NlX2ZpbGUgPC0gZmlsZS5wYXRoKGRhdGFfZGlyLCAiZGdlIiwgIlNDUENQMDAwMDA1X2ludGVncmF0ZWRfZmFzdG1ubl9zY2UucmRzIikKaW50ZWdyYXRlZF9zY2UgPC0gcmVhZHI6OnJlYWRfcmRzKGludGVncmF0ZWRfc2NlX2ZpbGUpCmBgYAoKYGBge3J9CiMgcHV0IHRoZSBpbnRlZ3JhdGVkIFBDQSBhbmQgVU1BUCBpbnRvIHRoZSBjb21iaW5lZCBvYmplY3Qgc28gYWxsIHRoZSBkYXRhIGlzIHRvZ2V0aGVyIApyZWR1Y2VkRGltKGNvbWJpbmVkX3NjZSwgImZhc3RNTk5fUENBIikgPC0gcmVkdWNlZERpbShpbnRlZ3JhdGVkX3NjZSwgImZhc3Rtbm5fUENBIikKcmVkdWNlZERpbShjb21iaW5lZF9zY2UsICJmYXN0TU5OX1VNQVAiKSA8LSByZWR1Y2VkRGltKGludGVncmF0ZWRfc2NlLCAiZmFzdG1ubl9VTUFQIikKcmVkdWNlZERpbU5hbWVzKGNvbWJpbmVkX3NjZSkKYGBgCgpgYGB7cn0KIyBmaWxlIHBhdGggdG8gc2FtcGxlIGxldmVsIG1ldGFkYXRhIApzYW1wbGVfbWV0YWRhdGFfZmlsZSA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJybXNfc2FtcGxlX21ldGFkYXRhLnRzdiIpCgojIHJlYWQgaW4gc2FtcGxlIGxldmVsIG1ldGFkYXRhIHRvIHVzZSBmb3IgREUgCnNhbXBsZV9tZXRhZGF0YSA8LSByZWFkcjo6cmVhZF90c3Yoc2FtcGxlX21ldGFkYXRhX2ZpbGUpCmBgYApMZXQncyB0YWtlIHRoZSBzYW1wbGUgbWV0YWRhdGEgZmlsZSBhbmQgYWRkIGluIHRoZSBtZXRhZGF0YSB0byB0aGUgYGNvbERhdGFgIG9mIHRoZSBjb21iaW5lZCBTQ0Ugb2JqZWN0LiAKV2hpbGUgZG9pbmcgdGhpcyB3ZSBjYW4gYWxzbyBkbyBzb21lIG1vZGlmeWluZyBvZiB0aGUgY29sdW1ucyBpbiB0aGUgYGNvbERhdGFgLCBzdWNoIGFzIGNyZWF0aW5nIGEgYGNlbGx0eXBlX2Jyb2FkYCBhbmQgYGNlbGx0eXBlX2ZpbmVgIGNvbHVtbiB0byB1c2UgZm9yIHBsb3R0aW5nLiAgCgpgYGB7cn0KIyBjb2xsYXBzZSBjZWxsIHR5cGVzIApjb2xkYXRhX2RmIDwtIGNvbERhdGEoY29tYmluZWRfc2NlKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImNlbGxfaWQiKSAlPiUKICAjIHJlbW92ZSBhbnkgb2YgdGhlIHN1YnR5cGVzIHRvIGNyZWF0ZSBuZXcgY2VsbCB0eXBlIGNvbHVtbiAKICBkcGx5cjo6bXV0YXRlKGNlbGx0eXBlX2Jyb2FkID0gc3RyaW5ncjo6c3RyX3JlbW92ZShjZWxsdHlwZSwgIi1bQUJDRF0kIiksCiAgICAgICAgICAgICAgICBjZWxsdHlwZV9maW5lID0gY2VsbHR5cGUpICU+JQogICMgbWVyZ2Ugd2l0aCBzYW1wbGUgbWV0YWRhdGEgCiAgZHBseXI6OmxlZnRfam9pbihzYW1wbGVfbWV0YWRhdGEsIGJ5ID0gYygiYmF0Y2giID0gImxpYnJhcnlfaWQiKSkgJT4lCiAgICAjIHNpbXBsaWZ5IHN1YmRpYWdub3NpcyBmb3IgbGF0ZXIKICBkcGx5cjo6bXV0YXRlKGRpYWdub3Npc19ncm91cHMgPSBmYWN0b3IoCiAgICBkcGx5cjo6Y2FzZV93aGVuKAogICAgICBzdWJkaWFnbm9zaXMgPT0gIkFsdmVvbGFyIHJoYWJkb215b3NhcmNvbWEiIH4gIkFSTVMiLAogICAgICBzdWJkaWFnbm9zaXMgPT0gIkVtYnJ5b25hbCByaGFiZG9teW9zYXJjb21hIiB+ICJFUk1TIgogICAgKSkpCgojIGFkZCBiYWNrIHRvIHNjZSAKY29sRGF0YShjb21iaW5lZF9zY2UpIDwtIERhdGFGcmFtZShjb2xkYXRhX2RmLCByb3cubmFtZXMgPSBjb2xkYXRhX2RmJGNlbGxfaWQpCmBgYAoKTGV0J3MganVzdCBkbyBzb21lIGluaXRpYWwgZXhwbG9yYXRpb24gb2YgdGhlIGludGVncmF0ZWQgZGF0YXNldCB0aGF0IHdlIGFyZSB3b3JraW5nIHdpdGguIAoKYGBge3J9CiMgVU1BUCB0byBzaG93IGNlbGwgdHlwZSBhbmQgaW50ZWdyYXRpb24gIApzY2F0ZXI6OnBsb3RSZWR1Y2VkRGltKGNvbWJpbmVkX3NjZSwKICAgICAgICAgICAgICAgICAgICAgICBkaW1yZWQgPSAiZmFzdE1OTl9VTUFQIiwKICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXJfYnkgPSAiYmF0Y2giLAogICAgICAgICAgICAgICAgICAgICAgIHBvaW50X3NpemU9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICBwb2ludF9hbHBoYSA9IDAuMikKCmBgYAoKYGBge3J9CiMgc2hvdyBjZWxsIHR5cGVzIGluIHRoZSBpbnRlZ3JhdGVkIGRhdGFzZXQgCnNjYXRlcjo6cGxvdFJlZHVjZWREaW0oY29tYmluZWRfc2NlLAogICAgICAgICAgICAgICAgICAgICAgIGRpbXJlZCA9ICJmYXN0TU5OX1VNQVAiLAogICAgICAgICAgICAgICAgICAgICAgIGNvbG91cl9ieSA9ICJjZWxsdHlwZV9icm9hZCIsCiAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRfc2l6ZT0gMC41LCAKICAgICAgICAgICAgICAgICAgICAgICBwb2ludF9hbHBoYSA9IDAuNCkKCiMgZmFjZXQgdGhlIFVNQVAgYnkgc2FtcGxlIHRvIHNob3cgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjZWxsIHR5cGVzIGFjcm9zcyBlYWNoIHNhbXBsZQpzY2F0ZXI6OnBsb3RSZWR1Y2VkRGltKGNvbWJpbmVkX3NjZSwKICAgICAgICAgICAgICAgICAgICAgICBkaW1yZWQgPSAiZmFzdE1OTl9VTUFQIiwKICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXJfYnkgPSAiY2VsbHR5cGVfYnJvYWQiLAogICAgICAgICAgICAgICAgICAgICAgIHBvaW50X3NpemU9IDAuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRfYWxwaGEgPSAwLjQsCiAgICAgICAgICAgICAgICAgICAgICAgb3RoZXJfZmllbGRzID0gImJhdGNoIikgKwogIGZhY2V0X3dyYXAofiBiYXRjaCwgbnJvdyA9IDMpCmBgYAoKTG9va2luZyBhdCB0aGlzIHBsb3QgbGV0cyB1cyBrbm93IHdoaWNoIGNlbGwgdHlwZXMgYmVsb25nIHRvIHdoaWNoIHNhbXBsZSBhbmQgY2FuIGhlbHAgdXMgcGljayB3aGljaCBsaWJyYXJpZXMgd2UgbWlnaHQgd2FudCB0byBjb25zaWRlciBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uCkZyb20gdGhpcyB3ZSBzZWUgdGhhdCA2IG9mIHRoZSBsaWJyYXJpZXMgc2hvdyBhIHNwbGl0IGJldHdlZW4gYFR1bW9yX01lc29kZXJtYCwgYFR1bW9yX015b2JsYXN0YCBhbmQgYFR1bW9yX015b2N5dGVgLgozIGxpYnJhcmllcyBhcmUgbW9zdGx5IGBUdW1vcmAgYW5kIHRoZXkgcHJvYmFibHkgd2VyZW4ndCBhYmxlIHRvIGJlIGNsYXNzaWZpZWQgaW50byBzdWIgdHlwZXMuIApUaGVuIDEgbGlicmFyeSBoYXMgd2hhdCBhcHBlYXJzIHRvIGJlIGFsbCBgTkFgLiAKTGV0J3MganVzdCBsb29rIGF0IHRoZSBsaWJyYXJpZXMgdGhhdCBoYXZlIG15b2JsYXN0LCBtZXNvZGVybSwgYW5kIG15b2N5dGVzIGFuZCBzZWUgaWYgd2UgY2FuIGRvIHNvbWUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYmV0d2VlbiB0aG9zZSBsaWJyYXJpZXMuIAoKIyMgU2V0IHVwIG9mIGRhdGFzZXQgdG8gdXNlIGZvciBpbnRlZ3JhdGlvbgoKYGBge3J9CiMgZGVmaW5lIGxpYnJhcmllcyB0byBrZWVwCnNjcGNhX2xpYnJhcnlfaWRzIDwtIGMoCiAgIlNDUENMMDAwNDc5IiwKICAiU0NQQ0wwMDA0ODAiLAogICJTQ1BDTDAwMDQ4MSIsCiAgIlNDUENMMDAwNDg0IiwKICAiU0NQQ0wwMDA0ODgiLAogICJTQ1BDTDAwMDQ5MSIKKQoKIyBzdWJzZXQgc2NlIHRvIG9ubHkgY29udGFpbiBiYXRjaGVzIHdpdGggbGlicmFyeSBJRHMgb2YgaW50ZXJlc3QgCmJhdGNoZXNfdG9fa2VlcCA8LSBjb21iaW5lZF9zY2UkYmF0Y2ggJWluJSBzY3BjYV9saWJyYXJ5X2lkcwpybXNfc2NlIDwtIGNvbWJpbmVkX3NjZVssYmF0Y2hlc190b19rZWVwXQpgYGAKCkxldCdzIG1ha2Ugc29tZSBvZiB0aGUgc2FtZSBwbG90cyBhYm92ZSBhbmQgc2hvdyB0aGUgZGlzdHJpYnV0aW9uIG9mIGNlbGwgdHlwZXMuIAoKYGBge3J9CiMgVU1BUCB0byBzaG93IGNlbGwgdHlwZSBhbmQgaW50ZWdyYXRpb24gIApzY2F0ZXI6OnBsb3RSZWR1Y2VkRGltKHJtc19zY2UsCiAgICAgICAgICAgICAgICAgICAgICAgZGltcmVkID0gImZhc3RNTk5fVU1BUCIsCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyX2J5ID0gImJhdGNoIiwKICAgICAgICAgICAgICAgICAgICAgICBwb2ludF9zaXplPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRfYWxwaGEgPSAwLjIpCmBgYAoKYGBge3J9CnNjYXRlcjo6cGxvdFJlZHVjZWREaW0ocm1zX3NjZSwKICAgICAgICAgICAgICAgICAgICAgICBkaW1yZWQgPSAiZmFzdE1OTl9VTUFQIiwKICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXJfYnkgPSAiY2VsbHR5cGVfYnJvYWQiLAogICAgICAgICAgICAgICAgICAgICAgIHBvaW50X3NpemU9IDAuNSwgIAogICAgICAgICAgICAgICAgICAgICAgIHBvaW50X2FscGhhID0gMC40LAogICAgICAgICAgICAgICAgICAgICAgIG90aGVyX2ZpZWxkcyA9ICJiYXRjaCIpICsKICBmYWNldF93cmFwKH4gYmF0Y2gpCmBgYAoKSXQgbG9va3MgbGlrZSB0aGUgcGluayBjZWxscywgb3IgdGhlIGBUdW1vcl9NeW9ibGFzdGAgYXJlIHRoZSBtb3N0IGRvbWluYW50IGNlbGwgdHlwZSBhY3Jvc3MgdGhlIGxpYnJhcmllcy4gClRoaXMgbWlnaHQgYmUgYW4gaW50ZXJlc3RpbmcgZ3JvdXAgdG8gc3RhcnQgd2l0aCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uIApXZSBjYW4gY29uZmlybSB0aGlzIGJ5IGxvb2tpbmcgYXQgdGhlIHRvdGFsIG51bWJlcnMgb2YgY2VsbHMgaW4gZWFjaCBjZWxsIHR5cGUuIAoKYGBge3J9CmFzLmRhdGEuZnJhbWUoY29sRGF0YShybXNfc2NlKSkgJT4lCiAgZHBseXI6OmNvdW50KGNlbGx0eXBlX2Jyb2FkKQoKIyBjYW4gYWxzbyBwcmludCBvdXQgYSBzdW1tYXJ5IG9mIHRoZSBjZWxsIHR5cGVzIGFjcm9zcyBlYWNoIGxpYnJhcnkgCmNvbERhdGEocm1zX3NjZSkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGRwbHlyOjpjb3VudChiYXRjaCwgY2VsbHR5cGVfYnJvYWQpICU+JQogIGRwbHlyOjpncm91cF9zcGxpdChiYXRjaCkKYGBgCgpgVHVtb3JfTXlvYmxhc3RgIGlzIGRlZmluaXRlbHkgdGhlIHRvcCByZXByZXNlbnRlZCBjZWxsIHR5cGUgYWNyb3NzIHRoZSBsaWJyYXJpZXMuIApTbyB3ZSB3YW50IHRvIGxvb2sgYXQgbXlvYmxhc3RzIGFjcm9zcyBvdXIgbGlicmFyaWVzIGFuZCBzZWUgaWYgdGhlcmUgYXJlIGFueSBjaGFuZ2VzIGluIHRoZSBteW9ibGFzdCBwb3B1bGF0aW9uIGJldHdlZW4gc2FtcGxlcyBvZiBkaWZmZXJlbnQgdHlwZXMuIApUbyBkbyB0aGlzLCB3ZSBzaG91bGQgcHJvYmFibHkgbG9vayBhdCBvdXIgc2FtcGxlIG1ldGFkYXRhIHRvIHNlZSB3aGF0IHR5cGVzIG9mIHNhbXBsZXMgd2UgYXJlIGxvb2tpbmcgYXQuIAoKYGBge3J9CiMgTGV0J3MgbG9vayBhdCBzb21lIG9mIHRoZSBjbGluaWNhbCBzYW1wbGUgbWV0YWRhdGEgdGhhdCB3ZSBoYXZlIAp0YWJsZShybXNfc2NlJGRpYWdub3NpcykgIyBldmVyeXRoaW5nIGlzIFJNUyAKdGFibGUocm1zX3NjZSRzdWJkaWFnbm9zaXMpICMgc2FtcGxlIGlzIHNwbGl0IGJldHdlZW4gRVJNUyBhbmQgQVJNUyAKCiMgaG93IG1hbnkgb2YgZWFjaCwgMyBvZiBlYWNoIHN1YmRpYWdub3NpcywgdGhhdCdzIGVub3VnaCByZXBsaWNhdGVzIHNvIGxldCdzIGNvbXBhcmUhIApjb2xEYXRhKHJtc19zY2UpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBkcGx5cjo6Y291bnQoYmF0Y2gsIHN1YmRpYWdub3NpcykKYGBgCgpJdCBsb29rcyBsaWtlIHdlIGhhdmUgdGhyZWUgc2FtcGxlcyB0aGF0IGFyZSBmcm9tIEFSTVMgcGF0aWVudHMgYW5kIHRocmVlIHNhbXBsZXMgdGhhdCBhcmUgZnJvbSBFUk1TIHBhdGllbnRzLiAKCkEgZ29vZCBxdWVzdGlvbiB3ZSBjb3VsZCBhc2sgaXMgaWYgdGhlcmUgYXJlIGRpZmZlcmVuY2VzIGluIGdlbmUgZXhwcmVzc2lvbiBwYXR0ZXJucyBpbiB0aGUgTXlvYmxhc3QgcG9wdWxhdGlvbiBiZXR3ZWVuIEFSTVMgYW5kIEVSTVMgcGF0aWVudHMuIApJZiB3ZSB3YW50IHRvIGFzayB0aGF0IHF1ZXN0aW9uLCB0aGVuIHdlIG5lZWQgdG8gaGF2ZSByZXBsaWNhdGVzLCBwcmVmZXJhYmx5IGF0IGxlYXN0IDMgc2FtcGxlcyBmb3IgZWFjaCB0eXBlIG9mIHBhdGllbnQsIHdoaWNoIHdlIGRvISAKCkxldCdzIGdvIGFoZWFkIGFuZCB0cnkgaXQhIAoKIyMgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gQW5hbHlzaXMKCiMjIyBQc2V1ZG8tYnVsa2luZyAKCkJlZm9yZSB3ZSBjYW4gZG8gREUgYW5hbHlzaXMgdG8gY29tcGFyZSB0aGUgZ2VuZSBleHByZXNzaW9uIHByb2ZpbGVzIG9mIG15b2JsYXN0cyBpbiBBUk1TIHZzLiBFUk1TIHBhdGllbnRzLCB3ZSB3aWxsIG5lZWQgdG8gInBzZXVkby1idWxrIiB0aGUgZ2VuZSBjb3VudHMuIApQc2V1ZG8tYnVsa2luZyBjcmVhdGVzIGEgbmV3IGNvdW50cyBtYXRyaXggdGhhdCBjb250YWlucyB0aGUgdG90YWwgY291bnRzIGFjcm9zcyBhbGwgY2VsbHMgb2YgYSBnaXZlbiBsYWJlbCAoZS5nLiBjZWxsIHR5cGUpIGZvciBlYWNoIHNhbXBsZS4gClRoaXMgYWxsb3dzIHVzIHRvIHVzZSBzaW5nbGUtY2VsbCByZXNvbHV0aW9uIHRvIGRlZmluZSB0aGUgbGFiZWxzIGFuZCBzdW0gZ2VuZSBjb3VudHMgYWNyb3NzIGdyb3VwcyBvZiBjZWxscyBjb250YWluaW5nIHRoZSBzYW1lIGxhYmVsLCBhbmQgYXZvaWRzIGNvdW50aW5nIGVhY2ggY2VsbCBhcyBpdHMgb3duIHJlcGxpY2F0ZS4gCgpCZWxvdyBhcmUgYSBmZXcgcmVhc29ucyB3aHkgcHNldWRvLWJ1bGtpbmcgaXMgaGVscGZ1bDogCgotIFByb2R1Y2VzIGxhcmdlciBjb3VudHMgd2hpY2ggYXJlIG1vcmUgYW1lbmFibGUgdG8gc3RhbmRhcmQgbm9ybWFsaXphdGlvbiBhbmQgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gbWV0aG9kcyB1c2VkIGJ5IGJ1bGsgUk5BLXNlcXVlbmNpbmcuIAotICBDb2xsYXBzZXMgZ2VuZSBleHByZXNzaW9uIGNvdW50cyBieSBzYW1wbGUsIHNvIHRoYXQgc2FtcGxlcywgcmF0aGVyIHRoYW4gY2VsbHMsIHJlcHJlc2VudCByZXBsaWNhdGVzLgotIE1hc2tzIHZhcmlhbmNlIHdpdGhpbiBhIHNhbXBsZSB0byBoaWdobGlnaHQgdmFyaWFuY2UgYWNyb3NzIHNhbXBsZXMuCgpMZXQncyBzdGFydCB3aXRoIHRoZSBzaW1wbGUgY29tcGFyaXNvbiBvZiBsb29raW5nIGF0IG9uZSBjZWxsIHR5cGUgYW5kIGNvbXBhcmluZyBhY3Jvc3MgRVJNUyBhbmQgQVJNUyBzdWJ0eXBlcy4gCldoZW4gd2UgZG8gdGhpcywgd2Ugd2lsbCBmaXJzdCB3YW50IHRvIHBzZXVkby1idWxrIG91ciBkYXRhc2V0IHRvIGdyb3VwIG91ciBjZWxscyBieSBjZWxsIHR5cGUgYW5kIGJ5IHNhbXBsZS4KVGhlbiB3ZSBjYW4gc3Vic2V0IHRoZSBwc2V1ZG8tYnVsa2VkIGRhdGFzZXQgdG8ganVzdCBjb250YWluIG91ciBjZWxsIHR5cGUgb2YgaW50ZXJlc3QgYmVmb3JlIHByb2NlZWRpbmcuIAoKYGBge3J9CiMgZmlyc3QgbGV0J3Mgc3Vic2V0IG91ciBjb2xkYXRhIHRvIG9ubHkgaGF2ZSB0aGUgY29sdW1ucyB3ZSBjYXJlIGFib3V0IGluIHBzZXVkb2J1bGtpbmcgCnBzZXVkb2J1bGtlZF9jb2xkYXRhIDwtIGNvbERhdGEocm1zX3NjZSlbLCBjKCJjZWxsdHlwZV9icm9hZCIsICJiYXRjaCIpXQoKIyB0aGlzIGNyZWF0ZXMgYSBuZXcgU0NFIG9iamVjdCB0aGF0IGNvbnRhaW5zIHRoZSBwc2V1ZG9idWxrZWQgY291bnRzIGFjcm9zcyB0aGUgcHJvdmlkZWQgZ3JvdXBzIApwc2V1ZG9idWxrZWRfc2NlIDwtIHNjYXRlcjo6YWdncmVnYXRlQWNyb3NzQ2VsbHMocm1zX3NjZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQgPSBwc2V1ZG9idWxrZWRfY29sZGF0YSkKCiMgY29sdW1uIG5hbWVzIGFyZW4ndCBhdXRvbWF0aWNhbGx5IGFkZGVkLCBzbyBsZXQncyBhZGQgdGhlbSBpbiAKY29sbmFtZXMocHNldWRvYnVsa2VkX3NjZSkgPC0gcGFzdGUocHNldWRvYnVsa2VkX3NjZSRjZWxsdHlwZV9icm9hZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBzZXVkb2J1bGtlZF9zY2UkYmF0Y2gsIHNlcCA9ICItIikKCnBzZXVkb2J1bGtlZF9zY2UKYGBgCgpgYGB7cn0KIyBvbmx5IDM3IGNvbHVtbnMgaW4gdGhlIGNvdW50cyBhc3NheSAKY291bnRzKHBzZXVkb2J1bGtlZF9zY2UpWzE6MTAsIDE6MTBdCmBgYAoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgd2hhdCB0aGUgYGNvbERhdGFgIG5vdyBsb29rcyBsaWtlIGluIHRoZSBwc2V1ZG8tYnVsa2VkIFNDRSBvYmplY3QuIAoKYGBge3J9CiMgbm90ZSB0aGUgbmV3IGFkZGl0aW9uIG9mIHRoZSBjb2x1bW4gd2l0aCBudW1iZXIgb2YgY2VsbHMgcGVyIGdyb3VwIApjb2xEYXRhKHBzZXVkb2J1bGtlZF9zY2UpCmBgYAoKQmVmb3JlIHdlIGNhbiBwZXJmb3JtIERHRSBhbmFseXNpcywgd2Ugd2FudCB0byBkbyBzb21lIHF1YWxpdHkgY29udHJvbCBsaWtlIGZpbHRlcmluZyBvdXQgYW55IGdyb3VwcyBpbiB0aGUgcHNldWRvLWJ1bGtlZCBkYXRhc2V0IHRoYXQgbWF5IGhhdmUgYSBsb3cgbnVtYmVyIG9mIGNlbGxzLiAKCmBgYHtyfQojIGZpbHRlciBieSBudW1iZXIgb2YgY2VsbHMgCmZpbHRlcmVkX3BzZXVkb2J1bGtlZF9zY2UgPC0gcHNldWRvYnVsa2VkX3NjZVssIHBzZXVkb2J1bGtlZF9zY2UkbmNlbGxzID49IDEwXQpgYGAKCldlIGFsc28gd2FudCB0byBmaWx0ZXIgb3V0IGFueSBnZW5lcyB0aGF0IG1heSBiZSBsb3dseSBleHByZXNzZWQgaW4gb3VyIHBzZXVkby1idWxrZWQgZGF0YXNldC4KCmBgYHtyfQojIGZpbHRlciBvdXQgZ2VuZXMgdGhhdCBtaWdodCBiZSBsb3dseSBleHByZXNzZWQgdXNpbmcgZmlsdGVyYnlFeHByIGluIGBlZGdlUmAKIyBwcm92aWRlIHNhbXBsZSBncm91cHMgZnJvbSBTQ0UgdG8gZGV0ZXJtaW5lIG1pbmltdW0gc2FtcGxlIHNpemUgdGhhdCBnZW5lcyBzaG91bGQgYmUgZXhwcmVzc2VkIGluIAojIHdlIGFyZSBncm91cGluZyBzYW1wbGVzIGFzIEVSTVMgdnMuIEFSTVMgc28gdXNlIHN1YmRpYWdub3NpcwpnZW5lc19maWx0ZXIgPC0gZWRnZVI6OmZpbHRlckJ5RXhwcihmaWx0ZXJlZF9wc2V1ZG9idWxrZWRfc2NlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBmaWx0ZXJlZF9wc2V1ZG9idWxrZWRfc2NlJHN1YmRpYWdub3NpcykKZmlsdGVyZWRfcHNldWRvYnVsa2VkX3NjZSA8LSBmaWx0ZXJlZF9wc2V1ZG9idWxrZWRfc2NlW2dlbmVzX2ZpbHRlcixdCgojIGxvb2tzIGxpa2UgdGhpcyBkb2Vzbid0IGFjdHVhbGx5IGZpbHRlciBhbnl0aGluZyB0aG91Z2gsIGJ1dCB3ZSBwcm9iYWJseSB3YW50IHRvIGtlZXAgdGhpcyBzdGVwIGluPyBpdCBzZWVtcyBpbXBvcnRhbnQgCmBgYAoKTWF5YmUgd2Ugc2hvdWxkIGV4cGxvcmUgY2hhbmdpbmcgZGVmYXVsdHMgaW4gaW5zdHJ1Y3Rpb24/IApBZGRpbmcgdGhlIGRlZmF1bHRzIGhlcmUgZm9yIGZ1dHVyZSByZWZlcmVuY2UuCgotIGBtaW4uY291bnQgPSAxMGA6IE1pbmltdW0gY291bnQgcmVxdWlyZWQgZm9yIGF0IGxlYXN0IHNvbWUgc2FtcGxlcy4KLSBgbWluLnRvdGFsLmNvdW50ID0gMTVgOiBNaW5pbXVtIHRvdGFsIGNvdW50IHJlcXVpcmVkLgotIGBsYXJnZS5uID0gMTBgOiBOdW1iZXIgb2Ygc2FtcGxlcyBwZXIgZ3JvdXAgdGhhdCBpcyBjb25zaWRlcmVkIHRvIGJlIOKAnGxhcmdl4oCdLgotIGBtaW4ucHJvcCA9IDAuN2A6IE1pbmltdW0gcHJvcG9ydGlvbiBvZiBzYW1wbGVzIGluIHRoZSBzbWFsbGVzdCBncm91cCB0aGF0IGV4cHJlc3MgdGhlIGdlbmUuCgoKVGhlIGxhc3QgZmlsdGVyaW5nIHdpbGwgYmUgdG8gZmlsdGVyIHRvIGp1c3QgdGhlIHN1YiB0eXBlIG9mIGNlbGxzIHRoYXQgd2UgYXJlIGludGVyZXN0ZWQgaW4uIAoKYGBge3J9CnR1bW9yX215b2JsYXN0X3NjZSA8LSBmaWx0ZXJlZF9wc2V1ZG9idWxrZWRfc2NlWywgZmlsdGVyZWRfcHNldWRvYnVsa2VkX3NjZSRjZWxsdHlwZV9icm9hZCA9PSAiVHVtb3JfTXlvYmxhc3QiXQp0YWJsZSh0dW1vcl9teW9ibGFzdF9zY2UkY2VsbHR5cGVfYnJvYWQpCmBgYAoKV2Ugc2hvdWxkIG5vdyBzZWUgdGhhdCB0aGVyZSBhcmUgb25seSA2IHNhbXBsZXMgbGVmdCB0aGF0IGFsbCBoYXZlIHRoZSBjZWxsIHR5cGUgYFR1bW9yX015b2JsYXN0YC4gCgojIyMgUGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiB3aXRoIGBlZGdlUmAKCk5vdGUgdGhhdCBhdCB0aGlzIHBvaW50IHdlIGNhbiB0cmVhdCBvdXIgZGF0YSBzZXQgbGlrZSBidWxrIFJOQS1zZXEgc2FtcGxlcyB3aGVyZSBlYWNoIGxpYnJhcnkgaXMgYSBzYW1wbGUgYW5kIHVzZSB0aGUgc2FtZSBtZXRob2RzIHdlIHdvdWxkIHVzZSBmb3IgYnVsaywgc3VjaCBhcyBgZWRnZVJgIGFuZCBgREVTZXEyYC4KRmlyc3QgbGV0J3MgdHJ5IGFuZCBkbyBzb21lIERFIGFuYWx5c2lzIHdpdGggYGVkZ2VSYC4KCmBgYHtyfQpkZ2VfbGlzdCA8LSBlZGdlUjo6REdFTGlzdChjb3VudHModHVtb3JfbXlvYmxhc3Rfc2NlKSwKICAgICAgICAgICAgICAgICAgICBzYW1wbGVzID0gY29sRGF0YSh0dW1vcl9teW9ibGFzdF9zY2UpKQoKIyBtYWtlIGNvbHVtbiBuYW1lcyBlcXVhbCB0byBiYXRjaCBpZHMsIGhlbHBmdWwgZm9yIHBsb3R0aW5nIGxhdGVyIApjb2xuYW1lcyhkZ2VfbGlzdCkgPC0gZGdlX2xpc3Qkc2FtcGxlcyRiYXRjaApkZ2VfbGlzdApgYGAKCk5vdyB3ZSBuZWVkIHRvIG5vcm1hbGl6ZSB0aGVzZSBjb3VudHMhIAoKVXNlIHRoZSB0cmltbWVkIG1lYW4gb2YgTS12YWx1ZXMgbWV0aG9kIHRvIGNvbXB1dGUgbm9ybWFsaXphdGlvbiBmYWN0b3JzLiAKQ291bnRzIGFyZSBub3cgbGFyZ2UgZW5vdWdoIHRvIGFwcGx5IGJ1bGsgbm9ybWFsaXphdGlvbiBtZXRob2RzIHJhdGhlciB0aGFuIHRoZSBkZWNvbnZvbHV0aW9uIG1ldGhvZCB0aGF0IHdhcyB1c2VkIG9uIGluZGl2aWR1YWwgbGlicmFyaWVzLiAKCmBgYHtyfQojIGNvbXB1dGUgc29tZSBub3JtYWxpemF0aW9uIGZhY3RvcnMgCmRnZV9saXN0IDwtIGVkZ2VSOjpjYWxjTm9ybUZhY3RvcnMoZGdlX2xpc3QpCiMgc2hvdWxkIG5vdyBzZWUgYSBuZXcgY29sdW1uLCBgbm9ybS5mYWN0b3JzYApkZ2VfbGlzdCRzYW1wbGVzCmBgYAoKQmVmb3JlIHdlIGRvIGFueSBER0UsIGxldCdzIGRvIHNvbWUgUUMgY2hlY2tzIHRvIG1ha2Ugc3VyZSBvdXIgZGF0YSBsb29rcyBva2F5LiAKV2UgbWFrZSBzb21lIE1lYW4gZGlmZmVyZW5jZSBwbG90cywgYnV0IEknbSBub3QgcmVhbGx5IGNvbnZpbmNlZCB3ZSBnZXQgYSB3aG9sZSBsb3QgZnJvbSB0aGVzZT8gCgpgYGB7cn0KIyBtZWFuIGRpZmZlcmVuY2UgcGxvdHMKcHVycnI6OndhbGsoMTpuY29sKGRnZV9saXN0KSwgCiAgICAgICAgICAgIH4gbGltbWE6OnBsb3RNRChkZ2VfbGlzdCwgY29sdW1uID0gLngpKQpgYGAKCmBgYHtyfQojIE1EUyBzY2FsaW5nIHBsb3QgCiMgZmlyc3QgY29tcHV0ZSBjb3VudHMgcGVyIG1pbGxpb24gCmNvdW50c19wZXJfbWlsbGlvbiA8LSBlZGdlUjo6Y3BtKGRnZV9saXN0LCBsb2cgPSBUUlVFKQoKbGltbWE6OnBsb3RNRFMoY291bnRzX3Blcl9taWxsaW9uLAogICAgICAgICMgYWRkIHNvbWUgY29sb3JzIGJhc2VkIG9uIG91ciB0d28gZ3JvdXBzCiAgICAgICAgY29sID0gaWZlbHNlKGRnZV9saXN0JHNhbXBsZXMkZGlhZ25vc2lzX2dyb3VwcyA9PSAiQVJNUyIsICJibGFjayIsICJyZWQiKSkKYGBgCgpMZXQncyBkbyBzb21lIERFIG5vdyEgCgpgYGB7cn0KIyBzZXQgdXAgZGVzaWduIG1hdHJpeCB0byBjb21wYXJlIGJldHdlZW4gQVJNUyBhbmQgRVJNUyBncm91cHMKZGVzaWduX21hdHJpeCA8LSBtb2RlbC5tYXRyaXgofmZhY3RvcihzdWJkaWFnbm9zaXMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZ2VfbGlzdCRzYW1wbGVzKQpkZXNpZ25fbWF0cml4CmBgYAoKRXN0aW1hdGUgZGlzcGVyc2lvbnMgdXNpbmcgYm90aCB0aGUgcXVhc2ktbGlrZWxpaG9vZChRTCkgZGlzcGVyc2lvbiBhbmQgdGhlIG5lZ2F0aXZlIGJpbm9taWFsKE5CKSBkaXNwZXJzaW9ucy4gCgpgYGB7cn0KIyBlc3RpbWF0ZSBOQiBkaXNwZXJzaW9ucyAKZGdlX2xpc3QgPC0gZWRnZVI6OmVzdGltYXRlRGlzcChkZ2VfbGlzdCwgZGVzaWduID0gZGVzaWduX21hdHJpeCkKc3VtbWFyeShkZ2VfbGlzdCR0cmVuZGVkLmRpc3BlcnNpb24pCmBgYAoKYGBge3J9CmVkZ2VSOjpwbG90QkNWKGRnZV9saXN0KQpgYGAKCmBgYHtyfQojIGVzdGltYXRlIHRoZSBxdWFzaS1saWtlbGlob29kIGRpc3BlcnNpb25zCmdsbV9maXQgPC0gZWRnZVI6OmdsbVFMRml0KGRnZV9saXN0LCBkZXNpZ25fbWF0cml4LCByb2J1c3QgPSBUUlVFKQplZGdlUjo6cGxvdFFMRGlzcChnbG1fZml0KQpgYGAKClRlc3QgZm9yIGRpZmZlcmVuY2VzIHVzaW5nIHRoZSBgZ2xtUUxGVGVzdGAgd2hlcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbnMgYXJlIGRlZmluZWQgYXMgdGhvc2Ugd2l0aCBub24temVybyBMRkMgYW5kIGEgRkRSIG9mIDwgNSUuCgpgYGB7cn0KcmVzdWx0cyA8LSBlZGdlUjo6Z2xtUUxGVGVzdChnbG1fZml0LCBjb2VmID0gbmNvbChkZXNpZ25fbWF0cml4KSkKc3VtbWFyeShsaW1tYTo6ZGVjaWRlVGVzdHMocmVzdWx0cykpCmBgYAoKYGBge3J9CmVkZ2VyX3Jlc3VsdHMgPC0gZWRnZVI6OnRvcFRhZ3MocmVzdWx0cywgbj0iSW5mIikkdGFibGUKZWRnZXJfcmVzdWx0c19zaWcgPC0gZWRnZXJfcmVzdWx0cyAlPiUKICBkcGx5cjo6ZmlsdGVyKEZEUiA8PSAwLjA1KQoKIyBwcmludCBvdXQgcmVzdWx0cyAKZWRnZXJfcmVzdWx0c19zaWcKYGBgCgojIyMgUGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiB3aXRoIERFU2VxMgoKTGV0J3MgZG8gdGhlIHNhbWUgdGhpbmcsIGJ1dCB0cnkgd2l0aCBERVNlcTIuCgpgYGB7cn0KIyBkZXNlcTIgcmVxdWlyZXMgdGhlIGNvbGRhdGEgb2JqZWN0IGFzIGEgc2VwYXJhdGUgaW5wdXQKY29sZGF0YV9kZiA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEodHVtb3JfbXlvYmxhc3Rfc2NlKSkKICAKIyBzZXQgdXAgdGhlIGRlc2VxIG9iamVjdCAKZGVzZXFfb2JqZWN0IDwtIERFU2VxMjo6REVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudHModHVtb3JfbXlvYmxhc3Rfc2NlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gY29sZGF0YV9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IGRpYWdub3Npc19ncm91cHMpCmBgYAoKVXNlIHRoZSBtZWRpYW4gb2YgcmF0aW9zIG1ldGhvZCBmb3IgY291bnQgbm9ybWFsaXphdGlvbiBmb2xsb3dlZCBieSByZWd1bGFyaXplZCBsb2cgdHJhbnNmb3JtYXRpb24uCgpgYGB7cn0KIyB3ZSBnZXQgYSB3YXJuaW5nIGlmIHdlIGRvbid0IHVzZSBsb2NhbCB0aGF0IGl0IGNhbid0IGZpbmQgYSBtb2RlbAojIHRoaXMgY291bGQgbGlrZWx5IGJlIGR1ZSB0byBiYXRjaCBlZmZlY3RzIHByZXNlbnQgaW4gdGhlIGRhdGEKbm9ybWFsaXplZF9vYmplY3QgPC0gREVTZXEyOjpybG9nKGRlc2VxX29iamVjdCwgYmxpbmQgPSBUUlVFLCBmaXQgPSAnbG9jYWwnKQpgYGAKCkV2YWx1YXRlIFFDIGhlcmUgdXNpbmcgUENBLiAKSGVyZSB3ZSBjYW4gbGFiZWwgYnkgdGhlIGRpYWdub3NpcyBncm91cHMgdG8gc2hvdyB0aGF0IHRoZSBoaWdoZXN0IGFtb3VudCBvZiB2YXJpYW5jZSBpcyBkdWUgdG8gZGlmZmVyZW50IGRpYWdub3NpcyB0eXBlcy4gCgpgYGB7cn0KREVTZXEyOjpwbG90UENBKG5vcm1hbGl6ZWRfb2JqZWN0LCBpbnRncm91cCA9ICJkaWFnbm9zaXNfZ3JvdXBzIikKYGBgCgpXZSBjYW4gYWxzbyBsYWJlbCBieSBvdGhlciBtZXRhZGF0YSB0byBzZWUgaWYgdGhlcmUgYXJlIG90aGVyIGZhY3RvcnMgdGhhdCBtaWdodCBiZSByZWxhdGVkIHRvIHZhcmlhdGlvbi4KCmBgYHtyfQpERVNlcTI6OnBsb3RQQ0Eobm9ybWFsaXplZF9vYmplY3QsIGludGdyb3VwID0gImRpc2Vhc2VfdGltaW5nIikKYGBgCgpgYGB7cn0KIyBydW4gREVTZXEKIyB1c2UgdGhlIHNhbWUgZml0IHR5cGUgYXMgd2l0aCBybG9nIApkZXNlcV9vYmplY3QgPC0gREVTZXEyOjpERVNlcShkZXNlcV9vYmplY3QsIGZpdFR5cGUgPSAnbG9jYWwnKQpgYGAKTmV4dCB3ZSB0YWtlIGEgbG9vayBhdCB0aGUgZGlzcGVyc2lvbnMsIHdlIGV4cGVjdCB0byBzZWUgZGlzcGVyc2lvbnMgZGVjcmVhc2UgYXMgbWVhbnMgYXJlIGluY3JlYXNpbmcgYW5kIGZvbGxvdyB0aGUgbGluZSBvZiBiZXN0IGZpdC4gCkhlcmUgZGlzcGVyc2lvbnMgYXJlIGluY3JlYXNpbmcgYXMgbWVhbiBpcyBpbmNyZWFzaW5nIGJ1dCBhcmUgZm9sbG93aW5nIHRoZSBsaW5lIG9mIGJlc3QgZml0LCBzbyBub3QgdGhlIG1vc3QgaWRlYWwuLi4gcGVyaGFwcyB1bmRlcmx5aW5nIGJhdGNoIGVmZmVjdHMgYXJlIGhlcmUgYW5kIGFmZmVjdGluZyB0aGlzPyAKCmBgYHtyfQpwbG90RGlzcEVzdHMoZGVzZXFfb2JqZWN0KQpgYGAKCmBgYHtyfQpkZXNlcV9yZXN1bHRzIDwtIERFU2VxMjo6cmVzdWx0cyhkZXNlcV9vYmplY3QsIGFscGhhID0gMC4wNSkKREVTZXEyOjpyZXN1bHRzTmFtZXMoZGVzZXFfb2JqZWN0KQpzaHJpbmtfcmVzdWx0cyA8LSBERVNlcTI6OmxmY1NocmluayhkZXNlcV9vYmplY3QsIHJlcyA9IGRlc2VxX3Jlc3VsdHMsIGNvZWYgPSAyLCB0eXBlID0gImFwZWdsbSIpCmBgYAoKYGBge3J9CiMgbG9vayBhdCB3aGF0IGFjdHVhbGx5IGhhcHBlbmVkIHdoZW4gd2UgYXBwbHllZCBzaHJpbmthZ2UgKGFkanVzdG1lbnQgb2YgbGFyZ2UgZm9sZCBjaGFuZ2VzIHRoYXQgYXJlIG92ZXJlc3RpbWF0ZWQpCmNvbXBhcmlzb25fZGYgPC0gZGF0YS5mcmFtZSgKICBsZmNfb3JpZ2luYWwgPSBkZXNlcV9yZXN1bHRzJGxvZzJGb2xkQ2hhbmdlLAogIGxmY19zaHJ1bmtlbiA9IHNocmlua19yZXN1bHRzJGxvZzJGb2xkQ2hhbmdlLAogIGxvZ21lYW4gPSBsb2cxMChkZXNlcV9yZXN1bHRzJGJhc2VNZWFuKQogICkKCmdncGxvdChjb21wYXJpc29uX2RmLCAKICAgICAgIGFlcyh4ID0gbGZjX29yaWdpbmFsLCAKICAgICAgICAgICB5ID0gbGZjX3NocnVua2VuLCAKICAgICAgICAgICBjb2xvciA9IGxvZ21lYW4pKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMSkgKwogIHRoZW1lX2J3KCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsgCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC0xMCwxMCksIHlsaW0gPSBjKC0xMCwxMCkpCmBgYAoKTGV0J3MgZ2V0IGEgdGFibGUgb2YgdGhlIGdlbmVzIHRoYXQgYXJlIHNpZ25pZmljYW50LiAKYGBge3J9CmRlc2VxX3Jlc3VsdHMgPC0gc2hyaW5rX3Jlc3VsdHMgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJlbnNlbWJsX2lkIikKCmRlc2VxX3Jlc3VsdHNfc2lnIDwtIGRlc2VxX3Jlc3VsdHMgJT4lCiAgZHBseXI6OmZpbHRlcihwYWRqIDw9IDAuMDUpCgpkZXNlcV9yZXN1bHRzX3NpZwpgYGAKCgojIyBFdmFsdWF0aW5nIHRoZSBER0UgUmVzdWx0cyAKCiMjIyBDb21wYXJpbmcgbWV0aG9kcwoKQmVmb3JlIHdlIG1vdmUgb24sIGxldCdzIGNvbXBhcmUgdGhlIHJlc3VsdHMgYmV0d2VlbiB0aGUgdHdvIHR5cGVzIG9mIERFRyBhbmFseXNpcyB3ZSBkaWQuIApIZXJlIHdlIGNhbiBsb29rIGF0IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBgbG9nRkNgIGNhbGN1bGF0ZWQgd2l0aCBgZWRnZVJgIGFuZCB0aGUgbG9nRkMgY2FsY3VsYXRlZCB3aXRoIERFU2VxMiwgd2hpY2ggd2UgZXhwZWN0IHRvIGJlIHNpbWlsYXIuIApXZSBjYW4gdGhlbiBzZWUgd2hpY2ggZ2VuZXMgYXJlIGNhbGN1bGF0ZWQgYXMgc2lnbmlmaWNhbnQgd2l0aCBlYWNoIG1ldGhvZCBhbmQgaG93IHRoZXkgY29tcGFyZS4KCmBgYHtyfQplZGdlcl9yZXN1bHRzIDwtIGVkZ2VyX3Jlc3VsdHMgJT4lCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImVuc2VtYmxfaWQiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGVuc2VtYmxfaWQsIGxvZ0ZDLCBGRFIpICU+JQogIGRwbHlyOjpyZW5hbWUoImVkZ2VSX0xvZ0ZDIiA9ICJsb2dGQyIsCiAgICAgICAgICAgICAgICAiZWRnZVJfcGFkaiIgPSAiRkRSIikKCmNvbWJpbmVkX3Jlc3VsdHMgPC0gZGVzZXFfcmVzdWx0cyAlPiUKICBkcGx5cjo6c2VsZWN0KGVuc2VtYmxfaWQsIGxvZzJGb2xkQ2hhbmdlLCBwYWRqKSAlPiUKICBkcGx5cjo6cmVuYW1lKCJERVNlcV9Mb2dGQyIgPSAibG9nMkZvbGRDaGFuZ2UiLAogICAgICAgICAgICAgICAgIkRFU2VxX3BhZGoiID0gInBhZGoiKSAlPiUKICBkcGx5cjo6aW5uZXJfam9pbihlZGdlcl9yZXN1bHRzLCBieSA9IGMoImVuc2VtYmxfaWQiKSkgJT4lCiAgZHBseXI6Om11dGF0ZSgKICAgIHNpZ25pZmljYW5jZSA9IGRwbHlyOjpjYXNlX3doZW4oREVTZXFfcGFkaiA8PSAwLjA1ICYgZWRnZVJfcGFkaiA+IDAuMDUgfiAiREVTZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBERVNlcV9wYWRqID4gMC4wNSAmIGVkZ2VSX3BhZGogPD0gMC4wNSB+ICJlZGdlUiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERFU2VxX3BhZGogPD0gMC4wNSAmIGVkZ2VSX3BhZGogPD0gMC4wNSB+ICJib3RoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgREVTZXFfcGFkaiA+IDAuMDUgJiBlZGdlUl9wYWRqID4gMC4wNSB+ICJOb25lIikKICApCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoY29tYmluZWRfcmVzdWx0cywgYWVzKHggPSBERVNlcV9Mb2dGQywgeSA9IGVkZ2VSX0xvZ0ZDLCBjb2xvciA9IHNpZ25pZmljYW5jZSkpICsgCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKwogIHRoZW1lX2J3KCkKYGBgCgpIZXJlIHdlIHNlZSB0aGF0IGl0IGxvb2tzIGxpa2UgYGVkZ2VSYCB0YWtlcyBhIG1vcmUgY29uc2VydmF0aXZlIGFwcHJvYWNoIGFuZCB0aGF0IGFueSBnZW5lcyBzaWduaWZpY2FudCBpbiBgZWRnZVJgIGFyZSBhbHNvIHNpZ25pZmljYW50IGluIGBERVNlcWAuIApXZSBhbHNvIHNlZSB0aGF0IHRoZXJlJ3MgYW4gZXh0cmEgc3Vic2V0IG9mIGdlbmVzICh3aXRoIGxvd2VyIGFicyhsb2cgZm9sZCBjaGFuZ2VzKSkgdGhhdCBhcmUgaWRlbnRpZmllZCBhcyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaW4gYERFU2VxYCBvbmx5LiAKCiMjIyBFeHBsb3JpbmcgdGhlIGlkZW50aWZpZWQgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIAoKTGV0J3MgYWxzbyBkbyBzb21lIGluaXRpYWwgYW5hbHlzaXMgdG8gbG9vayBhdCB3aGF0IHR5cGVzIG9mIGdlbmVzIGFyZSBiZWluZyBpZGVudGlmaWVkIGFuZCBzZWUgaWYgd2hhdCBpcyBiZWluZyBpZGVudGlmaWVkIGFzIGFjdHVhbGx5IHVwL2Rvd24gcmVndWxhdGVkIGFwcGVhciB0byBjb3JyZWxhdGUgYmFjayB0byB0aGUgc2luZ2xlIGNlbGwgZGF0YS4gCgpUaGUgZmlyc3QgcGxvdCB3ZSdsbCBtYWtlIGlzIGEgdm9sY2FubyBwbG90IGFuZCB3ZSBjYW4gbGFiZWwgdGhlIHNpZ25pZmljYW50IGdlbmVzIHdpdGggdGhlaXIgZ2VuZSBzeW1ib2xzLiAKRm9yIG5vdywgbGV0J3MgdXNlIHRoZSBERVNlcSByZXN1bHRzLiAKCmBgYHtyfQpkZXNlcV9kZiA8LSBzaHJpbmtfcmVzdWx0cyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImVuc2VtYmxfaWQiKQoKc2NlX3Jvd2RhdGFfZGYgPC0gcm93RGF0YSh0dW1vcl9teW9ibGFzdF9zY2UpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiZW5zZW1ibF9pZCIpCgpkZXNlcV9kZiA8LSBkZXNlcV9kZiAlPiUKICBkcGx5cjo6bGVmdF9qb2luKHNjZV9yb3dkYXRhX2RmLCBieSA9ICJlbnNlbWJsX2lkIikKCmBgYAoKYGBge3J9CkVuaGFuY2VkVm9sY2Fubzo6RW5oYW5jZWRWb2xjYW5vKGRlc2VxX2RmLAogICAgICAgICAgICAgICAgeCA9ICdsb2cyRm9sZENoYW5nZScsICMgZm9sZCBjaGFuZ2Ugc3RhdGlzdGljIHRvIHBsb3QKICAgICAgICAgICAgICAgIHkgPSAncHZhbHVlJywgIyBzaWduaWZpY2FuY2UgdmFsdWVzCiAgICAgICAgICAgICAgICBsYWIgPSBkZXNlcV9kZiRnZW5lX3N5bWJvbC5TQ1BDTDAwMDQ3OCwgIyBsYWJlbHMgZm9yIHBvaW50cwogICAgICAgICAgICAgICAgcEN1dG9mZiA9IDFlLTA1LCAjIFRoZSBwIHZhbHVlIGN1dG9mZiB3ZSB3aWxsIHVzZSAoZGVmYXVsdCkKICAgICAgICAgICAgICAgIEZDY3V0b2ZmID0gMSwgIyBUaGUgZm9sZCBjaGFuZ2UgY3V0b2ZmIChkZWZhdWx0KQogICAgICAgICAgICAgICAgdGl0bGUgPSBOVUxMLCAjIG5vIHRpdGxlCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IE5VTEwsICMgb3Igc3VidGl0bGUKICAgICAgICAgICAgICAgIGNhcHRpb24gPSBOVUxMLCAjIG9yIGNhcHRpb24KICAgICAgICAgICAgICAgIGxhYlNpemUgPSAzICAjIHNtYWxsZXIgbGFiZWxzCiAgICAgICAgICAgICAgICApICsKICAjIGNoYW5nZSB0aGUgb3ZlcmFsbCB0aGVtZQogIHRoZW1lX2NsYXNzaWMoKSArCiAgIyBtb3ZlIHRoZSBsZWdlbmQgdG8gdGhlIGJvdHRvbQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpgYGAKCkxldCdzIG1ha2Ugc29tZSBVTUFQcyB3aGVyZSB3ZSBwbG90IGEgZ2VuZSB0aGF0J3MgaWRlbnRpZmllZCB0byBiZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaW4gYSBnaXZlbiBjZWxsIHR5cGUgYW5kIHNlZSB3aGF0IHRoZSBleHByZXNzaW9uIG9mIHRoYXQgZ2VuZSBpcyBhY3Jvc3Mgc2FtcGxlcy4gCgpgYGB7cn0KIyBhZGQgY29sdW1uIHRvIGNvbERhdGEgd2l0aCBleHByZXNzaW9uIG9mIGdlbmUgb2YgaW50ZXJlc3QgCiMgaGVyZSB3ZSB3aWxsIGFjdHVhbGx5IHB1dCBhbGwgb2YgdGhlIHNpZ25pZmljYW50IGdlbmVzIHNvIHRoZW4gd2UgY2FuIHBsb3QgYW55IG9mIHRoZW0gCnNpZ19leHByZXNzZWRfZ2VuZXMgPC0gZGVzZXFfZGYgJT4lCiAgZHBseXI6OmZpbHRlcihwdmFsdWUgPCAxZS01LAogICAgICAgICAgICAgICAgYWJzKGxvZzJGb2xkQ2hhbmdlKSA+IDIpICU+JQogIGRwbHlyOjpwdWxsKGVuc2VtYmxfaWQpCgpzaWdfZ2VuZXNfY291bnRzIDwtIGxvZ2NvdW50cyhjb21iaW5lZF9zY2Vbc2lnX2V4cHJlc3NlZF9nZW5lcyxdKSAlPiUKICBhcy5tYXRyaXgoKSAlPiUKICB0KCkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGRwbHlyOjptdXRhdGUoY2VsbF9pZCA9IGNvbG5hbWVzKGNvbWJpbmVkX3NjZSkpCiAgCmNvbWJpbmVkX2NvbGRhdGFfZGYgPC0gYXMuZGF0YS5mcmFtZShjb2xEYXRhKGNvbWJpbmVkX3NjZSkpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oc2lnX2dlbmVzX2NvdW50cywgYnkgPSAiY2VsbF9pZCIpCgojIGFkZCBiYWNrIG1vZGlmaWVkIGNvbGRhdGEgY29udGFpbmluZyBnZW5lIGV4cHJlc3Npb24gZm9yIHNpZyBnZW5lcyAKY29sRGF0YShjb21iaW5lZF9zY2UpIDwtIERhdGFGcmFtZShjb21iaW5lZF9jb2xkYXRhX2RmLCByb3cubmFtZXMgPSBjb21iaW5lZF9jb2xkYXRhX2RmJGNlbGxfaWQpCmBgYAoKVGhlIGZpcnN0IHBsb3Qgd2Ugd2lsbCBtYWtlIGlzIHdpdGggU09YNSB3aGljaCBpcyBmb3VuZCBpbiBib3RoIGBlZGdlUmAgYW5kIGBERVNlcWAgCgpgYGB7cn0KIyBmaWx0ZXIgdG8ganVzdCBteW9ibGFzdCBjZWxscyBhbmQgcmVtb3ZlIGFueSBOQSdzIGJlZm9yZSBwbG90dGluZwpteW9ibGFzdF9jb21iaW5lZF9zY2UgPC0gY29tYmluZWRfc2NlWywgd2hpY2goY29tYmluZWRfc2NlJGNlbGx0eXBlX2Jyb2FkID09ICJUdW1vcl9NeW9ibGFzdCIpXQoKIyBmaXJzdCBsb29rIGF0IGp1c3QgQVJNUyB2cy4gRVJNUyAKc2NhdGVyOjpwbG90UmVkdWNlZERpbShteW9ibGFzdF9jb21iaW5lZF9zY2UsCiAgICAgICAgICAgICAgICAgICAgICAgZGltcmVkID0gImZhc3RNTk5fVU1BUCIsCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyX2J5ID0gIkVOU0cwMDAwMDEzNDUzMiIsICNTT1g1CiAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRfc2l6ZT0gMC41LAogICAgICAgICAgICAgICAgICAgICAgIHBvaW50X2FscGhhID0gMC40LAogICAgICAgICAgICAgICAgICAgICAgIG90aGVyX2ZpZWxkcyA9ICJkaWFnbm9zaXNfZ3JvdXBzIikgKwogIGZhY2V0X3dyYXAofiBkaWFnbm9zaXNfZ3JvdXBzLCBucm93ID0gMykKYGBgCgpgYGB7cn0KIyBub3cgbGV0J3MgbG9vayBhdCB0aGUgc2FtZSBnZW5lIGFjcm9zcyBlYWNoIHNhbXBsZSAKc2NhdGVyOjpwbG90UmVkdWNlZERpbShteW9ibGFzdF9jb21iaW5lZF9zY2UsCiAgICAgICAgICAgICAgICAgICAgICAgZGltcmVkID0gImZhc3RNTk5fVU1BUCIsCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyX2J5ID0gIkVOU0cwMDAwMDEzNDUzMiIsICNTT1g1CiAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRfc2l6ZT0gMC41LAogICAgICAgICAgICAgICAgICAgICAgIHBvaW50X2FscGhhID0gMC40LAogICAgICAgICAgICAgICAgICAgICAgIG90aGVyX2ZpZWxkcyA9IGMoImRpYWdub3Npc19ncm91cHMiLCAiYmF0Y2giKSkgKwogIGZhY2V0X3dyYXAofiBkaWFnbm9zaXNfZ3JvdXBzICsgYmF0Y2gsIG5yb3cgPSAzLCBkaXIgPSAndicpCmBgYAoKV2hhdCBhYm91dCBzb21lIGdlbmVzIHRoYXQgYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpbiBvbmx5IGBERVNlcWAgYnV0IG5vdCBgZWRnZVJgPyAKCmBgYHtyfQojIHBpY2sgb3V0IHRoZSB0b3AgZ2VuZXMgdGhhdCBhcmUgaWRlbnRpZmllZCBhcyBERSBpbiBvbmx5IERFU2VxIGFuZCBwbG90IHRob3NlIApjb21iaW5lZF9yZXN1bHRzICU+JQogIGRwbHlyOjpmaWx0ZXIoc2lnbmlmaWNhbmNlID09ICJERVNlcSIpICU+JQogIGRwbHlyOjphcnJhbmdlKERFU2VxX0xvZ0ZDKSAKYGBgCgpgYGB7cn0KIyB0b3AgZG93bnJlZ3VsYXRlZCBnZW5lIApzY2F0ZXI6OnBsb3RSZWR1Y2VkRGltKG15b2JsYXN0X2NvbWJpbmVkX3NjZSwKICAgICAgICAgICAgICAgICAgICAgICBkaW1yZWQgPSAiZmFzdE1OTl9VTUFQIiwKICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXJfYnkgPSAiRU5TRzAwMDAwMTgyMTk3IiwgI0VYVDEKICAgICAgICAgICAgICAgICAgICAgICBwb2ludF9zaXplPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgcG9pbnRfYWxwaGEgPSAwLjQsCiAgICAgICAgICAgICAgICAgICAgICAgb3RoZXJfZmllbGRzID0gYygiZGlhZ25vc2lzX2dyb3VwcyIsICJiYXRjaCIpKSArCiAgZmFjZXRfd3JhcCh+IGJhdGNoICsgZGlhZ25vc2lzX2dyb3VwcywgbnJvdyA9IDMpCmBgYAoKYGBge3J9CnNjYXRlcjo6cGxvdFJlZHVjZWREaW0obXlvYmxhc3RfY29tYmluZWRfc2NlLAogICAgICAgICAgICAgICAgICAgICAgIGRpbXJlZCA9ICJmYXN0TU5OX1VNQVAiLAogICAgICAgICAgICAgICAgICAgICAgIGNvbG91cl9ieSA9ICJFTlNHMDAwMDAxNjE2NzEiLCAjRVhUMQogICAgICAgICAgICAgICAgICAgICAgIHBvaW50X3NpemU9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICBwb2ludF9hbHBoYSA9IDAuNCwKICAgICAgICAgICAgICAgICAgICAgICBvdGhlcl9maWVsZHMgPSBjKCJkaWFnbm9zaXNfZ3JvdXBzIiwgImJhdGNoIikpICsKICBmYWNldF93cmFwKH4gYmF0Y2ggKyBkaWFnbm9zaXNfZ3JvdXBzLCBucm93ID0gMykKYGBgCgoKIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgYSBzZWNvbmRhcnkgY2VsbCB0eXBlIAoKV2UgY2FuIGFsc28gbG9vayBhdCBvdGhlciBjZWxsIHR5cGVzIGluIG91ciBkYXRhc2V0IGFuZCBzZXQgdXAgdGhlIHNhbWUgY29tcGFyaXNvbiBhY3Jvc3MgZGlzZWFzZSBzdGF0ZXMgdG8gaWRlbnRpZnkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLiAKRm9yIHNpbXBsaWNpdHkgd2Ugd2lsbCBqdXN0IHVzZSBgREVTZXEyYCBoZXJlLiAKCmBgYHtyfQojIHBpY2sgb3V0IHRoZSBuZXh0IG1vc3QgcHJvbWluZW50IGNlbGwgdHlwZSB0byB1c2UgCmNvbERhdGEoZmlsdGVyZWRfcHNldWRvYnVsa2VkX3NjZSkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGRwbHlyOjpncm91cF9ieShjZWxsdHlwZV9icm9hZCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZSh0b3RhbF9jZWxscyA9IHN1bShuY2VsbHMpKQpgYGAKCkZyb20gdGhpcyB0YWJsZSwgaXQgbG9va3MgbGlrZSB0aGUgdHVtb3Igc3ViIHR5cGVzIGFyZSBnb2luZyB0byBiZSB0aGUgbW9zdCBwcm9taXNpbmcuIAoKV2UncmUgZ29pbmcgdG8gcmVwZWF0IHRoZSBzYW1lIGFuYWx5c2lzIHdlIGRpZCBhYm92ZSBqdXN0IHVzaW5nIHRoZSBteW9jeXRlcyBpbnN0ZWFkLiAKSSBjb3VsZG4ndCBjb3B5IGFuZCBwYXN0ZSBzaW5jZSB3ZSB3ZXJlIGRvaW5nIGl0IGFnYWluLCBzbyBoZXJlJ3MgYSBmdW5jdGlvbiB0byByZXBlYXQgdGhlIGFuYWx5c2lzIGFuZCB3ZSBjYW4gcnVuIGl0IGFjcm9zcyBhbGwgY2VsbCB0eXBlcyBvZiBpbnRlcmVzdCBpbiB0aGUgYW5hbHlzaXMuIAoKYGBge3J9CnJ1bl9kZXNlcSA8LSBmdW5jdGlvbihzY2UsIGNlbGx0eXBlKXsKICAKICAjIHN1YnNldCB0byBjZWxsIHR5cGUgb2YgaW50ZXJlc3QKICBjZWxsdHlwZV9zY2UgPC0gc2NlWywgc2NlJGNlbGx0eXBlX2Jyb2FkID09IGNlbGx0eXBlXQogIAogIGNvbGRhdGFfZGYgPC0gYXMuZGF0YS5mcmFtZShjb2xEYXRhKGNlbGx0eXBlX3NjZSkpCiAgCiAgIyBjcmVhdGUgZGVzZXEgb2JqZWN0CiAgZGVzZXFfb2JqZWN0IDwtIERFU2VxMjo6REVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudHMoY2VsbHR5cGVfc2NlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gY29sZGF0YV9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IGRpYWdub3Npc19ncm91cHMpCiAgIyBwZXJmb3JtIGRlc2VxCiAgZGVzZXFfb2JqZWN0IDwtIERFU2VxMjo6REVTZXEoZGVzZXFfb2JqZWN0LCBmaXRUeXBlID0gJ2xvY2FsJykKICAKICAjIGV4dHJhY3QgcmVzdWx0cwogIGRlc2VxX3Jlc3VsdHMgPC0gREVTZXEyOjpyZXN1bHRzKGRlc2VxX29iamVjdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjA1KQogICMgc2hyaW5rIGFuZCBzZXQgdXAgZGF0YSBmcmFtZQogIHNocmlua19yZXN1bHRzIDwtIERFU2VxMjo6bGZjU2hyaW5rKGRlc2VxX29iamVjdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzID0gZGVzZXFfcmVzdWx0cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29lZiA9IDIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiYXBlZ2xtIikgJT4lCiAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiZW5zZW1ibF9pZCIpCiAgCiAgIyBnZXQgcm93ZGF0YSBmcm9tIHNjZSB0byBnZXQgZ2VuZSBzeW1ib2wgCiAgc2NlX3Jvd2RhdGFfZGYgPC0gcm93RGF0YShjZWxsdHlwZV9zY2UpICU+JQogICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImVuc2VtYmxfaWQiKSAlPiUKICAgICMgb25seSBzZWxlY3QgY29sdW1ucyB3ZSB3YW50LCB0aGUgZ2VuZSBzeW1ib2wgY29sdW1uIHdpbGwgYmUgbW9kaWZpZWQgaW4gdGhlIHNldHVwIHNjcmlwdCAKICAgIGRwbHlyOjpzZWxlY3QoZW5zZW1ibF9pZCwgZ2VuZV9zeW1ib2wuU0NQQ0wwMDA0NzgpICU+JQogICAgZHBseXI6OnJlbmFtZShnZW5lX3N5bWJvbCA9IGdlbmVfc3ltYm9sLlNDUENMMDAwNDc4KQogIAogICMgam9pbiB0b2dldGhlciBieSBlbnNlbWJsIGlkLCBhZGRpbmcgaW4gcm93ZGF0YSBpbmNsdWRlcyBnZW5lIHN5bWJvbCAKICBkZXNlcV9yZXN1bHRzX2RmIDwtIHNocmlua19yZXN1bHRzICU+JQogICAgZHBseXI6OmxlZnRfam9pbihzY2Vfcm93ZGF0YV9kZiwgYnkgPSAiZW5zZW1ibF9pZCIpCiAgCiAgcmV0dXJuKGRlc2VxX3Jlc3VsdHNfZGYpCiAgCn0KYGBgCgoKYGBge3J9CiMgb25seSBkbyB0aGUgdG9wIDIgY2VsbCB0eXBlcyByaWdodCBub3csIGhhdmUgdG8gZmlndXJlIG91dCBob3cgdG8gZmlsdGVyIG91dCBzb21lIHRoYXQgYXJlbid0IHJlcHJlc2VudGVkIGFjcm9zcyBhbGwgc2FtcGxlcyBpZiB3ZSB3YW50IHRvIGRvIGFsbCBvZiB0aGVtIApjZWxsdHlwZXMgPC0gYygiVHVtb3JfTXlvYmxhc3QiLCAiVHVtb3JfTWVzb2Rlcm0iLCAiVHVtb3JfTXlvY3l0ZSIpCgojIGNyZWF0ZSBhIGxpc3Qgb2YgZGF0YWZyYW1lcywgb25lIGZvciBlYWNoIGNlbGwgdHlwZSAKY2VsbHR5cGVzX2Rlc2VxX2xpc3QgPC0gcHVycnI6Om1hcChjZWxsdHlwZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfiBydW5fZGVzZXEoc2NlID0gZmlsdGVyZWRfcHNldWRvYnVsa2VkX3NjZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsdHlwZSA9IC54KSkgJT4lCiAgc2V0X25hbWVzKGNlbGx0eXBlcykKCiMgY29tYmluZSByZXN1bHRzIGludG8gb25lIGRhdGFmcmFtZQpjZWxsdHlwZXNfZGVzZXFfZGYgPC0gZHBseXI6OmJpbmRfcm93cyhjZWxsdHlwZXNfZGVzZXFfbGlzdCwgLmlkID0gImNlbGx0eXBlX2Jyb2FkIikKYGBgCgpgYGB7cn0KY2VsbHR5cGVzX2Rlc2VxX3NpZ19kZiA8LSBjZWxsdHlwZXNfZGVzZXFfZGYgJT4lCiAgZHBseXI6OmZpbHRlcihwYWRqIDw9IDAuMDEpCgojIHByaW50IG91dCBzaWduaWZpY2FudCBnZW5lcyB3aXRoIGRpcmVjdGlvbiBvZiBmb2xkIGNoYW5nZSBhbmQgZ3JvdXAgYnkgY2VsbCB0eXBlcyAKc3VtbWFyaXplZF9yZXN1bHRzX2RmIDwtIGNlbGx0eXBlc19kZXNlcV9zaWdfZGYgJT4lCiAgZHBseXI6Om11dGF0ZShkaXJlY3Rpb24gPSBpZmVsc2UobG9nMkZvbGRDaGFuZ2UgPiAxLCAiVXAiLCAiRG93biIpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoZW5zZW1ibF9pZCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZSh0b3RhbF9jZWxsdHlwZXMgPSBsZW5ndGgoY2VsbHR5cGVfYnJvYWQpLAogICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gZGlyZWN0aW9uLAogICAgICAgICAgICAgICAgICAgY2VsbHR5cGVfYnJvYWQgPSBjZWxsdHlwZV9icm9hZCwKICAgICAgICAgICAgICAgICAgIGdlbmVfc3ltYm9sID0gZ2VuZV9zeW1ib2wsCiAgICAgICAgICAgICAgICAgICBsb2cyRm9sZENoYW5nZSA9IGxvZzJGb2xkQ2hhbmdlKQoKIyBqdXN0IGEgcXVpY2sgc3VtbWFyeSBvZiB3aGF0cyBzaGFyZWQgdnMuIG5vdCBzaGFyZWQgCnN1bW1hcml6ZWRfcmVzdWx0c19kZiAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjpjb3VudChkaXJlY3Rpb24sIHRvdGFsX2NlbGx0eXBlcykKYGBgCgpXZSBhbHJlYWR5IGFkZGVkIHRoZSBjb3VudHMgZm9yIHNvbWUgZ2VuZXMgdG8gdGhlIGBjb21iaW5lZF9zY2VgIG9iamVjdCBzbyB3ZSBjYW4gdXNlIHRoYXQgdG8gbWFrZSBzb21lIHBsb3RzLiAKU3RhcnRpbmcgd2l0aCBgRk9YTzNgLCBpbiB0aGUgc2FtZSBmYW1pbHkgYXMgdGhlIGZ1c2lvbiBwYXJ0bmVyIChgUEFYMy9QQVg3LUZPWE8xYCkgZm91bmQgaW4gQVJNUy4gCgpgYGB7cn0KIyBzdWJzZXQgdG8ganVzdCB0dW1vciBjZWxsdHlwZXMgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbgp0dW1vcl9zY2UgPC0gY29tYmluZWRfc2NlWywgd2hpY2goY29tYmluZWRfc2NlJGNlbGx0eXBlX2Jyb2FkICVpbiUgY2VsbHR5cGVzKV0KCiMgdmlvbGluIHBsb3QgYWNyb3NzIGNlbGwgdHlwZXMgCnNjYXRlcjo6cGxvdEV4cHJlc3Npb24odHVtb3Jfc2NlLAogICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIkVOU0cwMDAwMDExODY4OSIsICNGT1hPMwogICAgICAgICAgICAgICAgICAgICAgIHggPSAiZGlhZ25vc2lzX2dyb3VwcyIsIAogICAgICAgICAgICAgICAgICAgICAgIGNvbG91cl9ieSA9ICJkaWFnbm9zaXNfZ3JvdXBzIiwKICAgICAgICAgICAgICAgICAgICAgICBvdGhlcl9maWVsZHMgPSAiY2VsbHR5cGVfYnJvYWQiLAogICAgICAgICAgICAgICAgICAgICAgIHBvaW50X3NpemUgPSAwLjEpICsKICBmYWNldF93cmFwKH4gY2VsbHR5cGVfYnJvYWQpCmBgYAoKQXMgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlc3VsdHMgc2hvd2VkLCBgRk9YTzFgIGlzIHVwIHJlZ3VsYXRlZCBpbiBBUk1TIGluIG1lc29kZXJtIGNlbGxzIGFuZCBteW9ibGFzdHMsIGJ1dCBhcHBlYXJzIHRvIGJlIGFsbW9zdCBlcXVhbGx5IGhpZ2hseSBleHByZXNzZWQgaW4gbXlvY3l0ZXMgaW4gYm90aCBkaXNlYXNlIHN1YiB0eXBlcy4gCgojIyBEaWZmZXJlbnRpYWwgYWJ1bmRhbmNlIG9mIGNlbGwgdHlwZXMgCgpJbiBhZGRpdGlvbiB0byBsb29raW5nIGF0IGRpZmZlcmVudGlhbCBleHByZXNzaW9uIG9mIGdlbmVzIGFjcm9zcyBjZWxsIHR5cGVzLCB3ZSBjYW4gYWxzbyBjb21wYXJlIHRoZSBkaXN0cmlidXRpb24gb2YgY2VsbCB0eXBlcyBpbiBlYWNoIHNhbXBsZSBhbmQgY29tcGFyZSBhY3Jvc3MgY29uZGl0aW9ucy4gCkluIHRoaXMgYW5hbHlzaXMsIHdlIHF1YW50aWZ5IHRoZSBudW1iZXIgb2YgY2VsbHMgYXNzaWduZWQgdG8gZWFjaCBjZWxsIHR5cGUgYW5kIG1lYXN1cmUgaWYgdGhlIGFidW5kYW5jZSBvZiBjZWxscyBhc3NpZ25lZCB0byBhIHNwZWNpZmljIGxhYmVsIGNoYW5nZXMgaW4gY29uZGl0aW9ucyAoZS5nLiBpbiBBUk1TIHZzLiBFUk1TKS4gCgpUaGUgZXhhbXBsZSB0aGF0IHdlIGFyZSBnb2luZyB0byB1c2UgaXMgdGhyb3VnaCB0aGUgYGVkZ2VSYCBwYWNrYWdlIGZvbGxvd2luZyB0aGUgZ3VpZGFuY2UgaW4gW09yY2hlc3RyYXRpbmcgU2luZ2xlIENlbGwgQW5hbHlzaXNdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL2Jvb2tzLzMuMTYvT1NDQS5tdWx0aXNhbXBsZS9kaWZmZXJlbnRpYWwtYWJ1bmRhbmNlLmh0bWwpLgoKSGVyZSB3ZSBuZWVkIHRvIHNldCB1cCBhbiBvYmplY3Qgd2hlcmUgdGhlIGNvdW50cyBhcmUgbm90IHJlYWRzIHBlciBnZW5lLCBidXQgY2VsbHMgcGVyIGxhYmVsLiAKCmBgYHtyfQojIGZpcnN0IHJlbW92ZSBhbnkgc2FtcGxlcyB3aXRoIE5BCmZpbHRlcmVkX2NvbWJpbmVkX3NjZSA8LSBjb21iaW5lZF9zY2VbLCAhaXMubmEoY29tYmluZWRfc2NlJGNlbGx0eXBlX2Jyb2FkKV0KIyBjcmVhdGUgYSB0YWJsZSBvZiBjZWxsIGFidW5kYW5jZXMgCiMgdHJlYXQgZWFjaCBjZWxsIHR5cGUgYXMgYSBnZW5lIApjZWxsX2FidW5kYW5jZSA8LSB0YWJsZShmaWx0ZXJlZF9jb21iaW5lZF9zY2UkY2VsbHR5cGVfYnJvYWQsIGZpbHRlcmVkX2NvbWJpbmVkX3NjZSRiYXRjaCkgJT4lCiAgdW5jbGFzcygpCgoKIyB1c2Ugb3JpZ2luYWwgc2FtcGxlIG1ldGFkYXRhIHRvIGdyYWIgZGlhZ25vc2lzIGZvciBlYWNoIGxpYnJhcnkgCnNhbXBsZV9tZXRhZGF0YSA8LSBzYW1wbGVfbWV0YWRhdGEgJT4lCiAgZHBseXI6OnNlbGVjdChsaWJyYXJ5X2lkLCBzdWJkaWFnbm9zaXMpICU+JQogIGRwbHlyOjptdXRhdGUoZGlhZ25vc2lzX2dyb3VwcyA9IGZhY3RvcigKICAgIGRwbHlyOjpjYXNlX3doZW4oCiAgICAgIHN1YmRpYWdub3NpcyA9PSAiQWx2ZW9sYXIgcmhhYmRvbXlvc2FyY29tYSIgfiAiQVJNUyIsCiAgICAgIHN1YmRpYWdub3NpcyA9PSAiRW1icnlvbmFsIHJoYWJkb215b3NhcmNvbWEiIH4gIkVSTVMiCiAgICApKSkgJT4lCiAgIyBmaWx0ZXIgb3V0IGFueSBOQSBjZWxsIHR5cGVzCiAgZHBseXI6OmZpbHRlcihsaWJyYXJ5X2lkICVpbiUgY29sbmFtZXMoY2VsbF9hYnVuZGFuY2UpKSAlPiUKICBEYXRhRnJhbWUoKQpyb3duYW1lcyhzYW1wbGVfbWV0YWRhdGEpIDwtIHNhbXBsZV9tZXRhZGF0YSRsaWJyYXJ5X2lkCgojIHNldCB1cCBER0VsaXN0CmFidW5kYW5jZV9kZ2VfbGlzdCA8LSBlZGdlUjo6REdFTGlzdChjZWxsX2FidW5kYW5jZSwgc2FtcGxlcyA9IHNhbXBsZV9tZXRhZGF0YSkKYGBgCgpGaWx0ZXIgb3V0IGFueSBsYWJlbHMgdGhhdCBhcmUgbG93IGFidW5kYW5jZS4gCgpgYGB7cn0KY2VsbHR5cGVzX2tlZXAgPC0gZWRnZVI6OmZpbHRlckJ5RXhwcihhYnVuZGFuY2VfZGdlX2xpc3QsIGdyb3VwID0gYWJ1bmRhbmNlX2RnZV9saXN0JHNhbXBsZXMkZGlhZ25vc2lzX2dyb3VwcykKYWJ1bmRhbmNlX2RnZV9saXN0IDwtIGFidW5kYW5jZV9kZ2VfbGlzdFtjZWxsdHlwZXNfa2VlcCwgXQpgYGAKClNldCB1cCB0aGUgZGVzaWduIG1hdHJpeCB0aGUgc2FtZSB3YXkgeW91IHdvdWxkIHdpdGggZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgYW5kIHBlcmZvcm0gdGhlIGFuYWx5c2lzLiAKCmBgYHtyfQpkZXNpZ25fbWF0cml4IDwtIG1vZGVsLm1hdHJpeCh+ZmFjdG9yKGRpYWdub3Npc19ncm91cHMpLCBhYnVuZGFuY2VfZGdlX2xpc3Qkc2FtcGxlcykKYWJ1bmRhbmNlX2RnZV9saXN0IDwtIGVkZ2VSOjplc3RpbWF0ZURpc3AoYWJ1bmRhbmNlX2RnZV9saXN0LCBkZXNpZ25fbWF0cml4LCB0cmVuZCA9ICJub25lIikKCmFidW5kYW5jZV9maXQgPC0gZWRnZVI6OmdsbVFMRml0KGFidW5kYW5jZV9kZ2VfbGlzdCwgcm9idXN0ID0gVFJVRSwgYWJ1bmRhbmNlLnRyZW5kID0gRkFMU0UpCmFidW5kYW5jZV9yZXN1bHRzIDwtIGVkZ2VSOjpnbG1RTEZUZXN0KGFidW5kYW5jZV9maXQsIGNvZWYgPSAyKQoKIyBwcmludCBvdXQgcmVzdWx0cyBhbmQgc3VtbWFyeQphYnVuZGFuY2VfcmVzdWx0cyR0YWJsZQpzdW1tYXJ5KGxpbW1hOjpkZWNpZGVUZXN0cyhhYnVuZGFuY2VfcmVzdWx0cykpCmBgYApJdCBkb2Vzbid0IGxvb2sgbGlrZSBhbnkgb2YgdGhlIHBvcHVsYXRpb25zIGFyZSBkaWZmZXJlbnRpYWxseSBhYnVuZGFudCBiZXR3ZWVuIHRoZSB0d28gY29uZGl0aW9ucy4gCkkgZGlkIGZpbmQgdGhpcyBzdGF0ZW1lbnQgZnJvbSB0aGUgcGFwZXIsICJBUk1TIHR1bW9ycyBjb250YWluZWQgc2lnbmlmaWNhbnRseSBmZXdlciBjZWxscyB3aXRoIHRoZSBtZXNvZGVybSBnZW5lIGV4cHJlc3Npb24gc2lnbmF0dXJlIGFuZCB3ZXJlIHNrZXdlZCB0b3dhcmRzIHRoZSBteW9jeXRlIHNpZ25hdHVyZS4iIApIb3dldmVyLCB0aGV5IGRvIHNob3cgcXVpdGUgYSBiaXQgb2YgdmFyaWFiaWxpdHkgaW4gdGhlIHN1cHBsZW1lbnRhbCBmaWd1cmVzIGFjcm9zcyB0aGUgc2FtcGxlcyBhbmQgdGhleSBvbmx5IHNob3cgdGhlbSBmb3IgdGhlIFBEWCBzYW1wbGVzIHdoaWxlIHRoZSBvbmVzIHdlIGFyZSBhbmFseXppbmcgYXJlIHRoZSBwYXRpZW50IHNhbXBsZXMuCldlIGRvIHNlZSBhIHBvc2l0aXZlIGZvbGQgY2hhbmdlIHRyZW5kaW5nIHRvd2FyZHMgYW4gaW5jcmVhc2UgaW4gdGhlIG1lc29kZXJtIHBvcHVsYXRpb24gaW4gRVJNUyBhbmQgYSBkZWNyZWFzZSBpbiBteW9jeXRlcyBpbiBFUk1TLCBhbGlnbmluZyB3aXRoIHRoZSBmaW5kaW5ncyBpbiB0aGUgcGFwZXIgKGFsdGhvdWdoIG5vdCBzaWduaWZpY2FudCB3aXRoIHRoZXNlIHNhbXBsZXMpLgoKIyMgU29tZSBjb25jbHVzaW9ucyBhbmQgZnV0dXJlIHRob3VnaHRzCgotIFRoZSBSTVMgZGF0YXNldCBzZWVtcyBsaWtlIGEgZ29vZCBkYXRhc2V0IHdlIGNhbiB1c2UgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzCi0gV2UgcHJvYmFibHkgZG9uJ3Qgd2FudCB0byBibGluZGx5IHVzZSBhbGwgbGlicmFyaWVzIHRoYXQgd2UgaGF2ZSBjdXJyZW50bHkgdXNlZCBmb3IgaW50ZWdyYXRpb24sIGJ1dCBzaG91bGQgdXNlIHRoZSA2IHNhbXBsZXMgdGhhdCBoYXZlIHZhcnlpbmcgc3ViIGRpYWdub3NpcyBhbmQgYWxzbyBzaGFyZSBzaW1pbGFyIGNlbGwgdHlwZXMKLSBIZXJlIHdlIGRpZCBzb21lIHByZWxpbWluYXJ5IGNvbXBhcmlzb25zIHVzaW5nIGBlZGdlUmAgYW5kIGBERVNlcWAsIGJ1dCB3ZSBwcm9iYWJseSBkb24ndCBuZWVkIHRvIGFjdHVhbGx5IGRvIHRoYXQgaW4gY2xhc3MsIHdlIHNob3VsZCBwaWNrIG9uZSBhbmQgc2hvdyBob3cgeW91IHdvdWxkIGRvIHRoZSBhbmFseXNpcyBmaXJzdCB0byBsb29rIGF0IG9uZSBjZWxsIHR5cGUgYmV0d2VlbiBleHBlcmltZW50YWwgY29uZGl0aW9ucyBhbmQgdGhlbiBhY3Jvc3MgbXVsdGlwbGUgY2VsbCB0eXBlcy8gbGFiZWxzCi0gYGVkZ2VSYCBpcyBhIGJpdCBtb3JlIGNvbnNlcnZhdGl2ZSBpbiBpdHMgYXBwcm9hY2ggdG8gaWRlbnRpZnkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzCi0gTG9va2luZyBhY3Jvc3MgdHVtb3Igc3VidHlwZXMgaWRlbnRpZmllcyBkaWZmZXJlbnQgZ3JvdXBzIG9mIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcwotIEEgcHJlbGltaW5hcnkgbG9vayBhdCBjZWxsIHR5cGUgY29tcG9zaXRpb24gY2hhbmdlcyByZXZlYWxzIHNvbWUgY2hhbmdlcyBjb25zaXN0ZW50IHdpdGggcHVibGljYXRpb24sIGJ1dCBub25lIHRoYXQgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgaW4gdGhpcyBzcGVjaWZpYyBjb2hvcnQKCgojIyBTZXNzaW9uIEluZm8gCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==